Defect | Summary | Date | Status |
---|---|---|---|
dr_400 | realloc with size zero problems
| 04/2013 | Closed |
dr_401 | "happens before" cannot be cyclic | 10/2012 | Closed |
dr_402 | memory model coherence is not aligned with C++11 | 10/2013 | Closed |
dr_403 | malloc() and free() in the memory model | 10/2012 | Closed |
dr_404 | joke fragment remains in a footnote | 10/2012 | Closed |
dr_405 | mutex specification not aligned with C++11 on total order | 10/2013 | Closed |
dr_406 | Visible sequences of side effects are redundant | 10/2014 | Open |
dr_407 | SC fences do not restrict modification order enough | 10/2014 | Open |
dr_408 | intra-thread synchronization | 04/2013 | Closed |
dr_409 | f(inf) is inf being a range error
| 10/2013 | Closed |
dr_410 | ilogb inconsistent with lrint , lround
| 04/2013 | Closed |
dr_411 | Predefined macro values | 02/2012 | Published |
dr_412 | #elif | 04/2013 | Closed |
dr_413 | initialization | 10/2014 | Closed |
dr_414 | typos in 6.27 threads.h
| 04/2013 | Closed |
dr_415 | Missing divide by zero entry in Annex J.2 | 04/2013 | Closed |
dr_416 | Proposed defect report regarding tss_t
| 10/2014 | Closed |
dr_417 | Missing entries in Annex J | 04/2013 | Closed |
dr_418 | fmod(0.,Nan) and fmod(Nan, infinity)
| 04/2013 | Closed |
dr_419 | What the heck is a "generic function"? | 10/2013 | Closed |
dr_420 | syntax error in specification of for-statement | 04/2013 | Closed |
dr_421 | initialization of atomic_flag
| 04/2014 | Closed |
dr_422 | initialization of atomic types | 04/2014 | Closed |
dr_423 | underspecification for qualified rvalues | 04/2014 | Open |
dr_424 | underspecification of tss_t
| 10/2014 | Closed |
dr_425 | no specification for the access to variables with temporary storage | 10/2013 | Closed |
dr_426 | G.5.1: -yv and -x/v are ambiguous
| 10/2014 | Closed |
dr_427 | Function Parameter and Return Value Assignments | 10/2014 | Open |
dr_428 | runtime-constraint issue with sprintf family of routines in Annex K | 04/2014 | Closed |
dr_429 | Should gets_s discard next input line when (s == NULL) ?
| 10/2014 | Closed |
dr_430 | getenv_s , maxsize should be allowed to be zero
| 04/2014 | Closed |
dr_431 | atomic_compare_exchange : What does it mean to say two structs compare equal?
| 10/2014 | Open |
dr_432 | Is 0.0 required to be a representable value?
| 04/2014 | Closed |
dr_433 | Issue with constraints for wide character function | 10/2014 | Closed |
dr_434 | Missing constraint w.r.t. Atomic | 10/2014 | Closed |
dr_435 | Missing constraint w.r.t. Imaginary | 10/2014 | Closed |
dr_436 | Request for interpretation of C11 6.8.5#6 | 10/2014 | Closed |
dr_437 | clock overflow
| 04/2014 | Open |
dr_438 | ungetc / ungetwc and file position after discarding push back
| 10/2014 | Review |
dr_439 | Issues with the definition of “full expression” | 04/2014 | Open |
dr_440 | Floating-point issues in C11 from PDTS 18661-1 UK review, Issue 1 | 10/2014 | Review |
dr_441 | Floating-point issues in C11 from PDTS 18661-1 UK review, Issue 2 | 10/2014 | Open |
dr_442 | Floating-point issues in C11 from PDTS 18661-1 UK review, Issue 3 | 10/2014 | Review |
dr_443 | Floating-point issues in C11 from PDTS 18661-1 UK review, Issue 4 | 10/2014 | Review |
dr_444 | Issues with alignment in C11, part 1 | 04/2014 | Open |
dr_445 | Issues with alignment in C11, part 2 | 10/2014 | Open |
dr_446 | Use byte instead of character for memcmp, memcpy | 10/2014 | Closed |
dr_447 | Boolean from complex | 10/2014 | Closed |
dr_448 | What are the semantics of a # non-directive? | 10/2014 | Open |
dr_449 | What is the value of TSS_DTOR_ITERATIONS for implementations with no maximum? | 10/2014 | Review |
dr_450 | tmpnam_s clears s[0] | 10/2014 | Open |
dr_451 | Instability of uninitialized automatic variables | 10/2014 | Review |
dr_452 | Effective Type in Loop Invariant | 10/2014 | Open |
dr_453 | Atomic flag type and operations | 10/2014 | Open |
dr_454 | ATOMIC_VAR_INIT (issues 3 and 4) | 10/2014 | Review |
dr_455 | ATOMIC_VAR_INIT issue 5 | 10/2014 | Open |
dr_456 | Compile time definition of UINTN_C(value)
| 10/2014 | Open |
dr_457 | The ctime_s function in Annex K defined incorrectly
| 10/2014 | Review |
dr_458 | ATOMIC_XXX_LOCK_FREE macros not constant expressions | 10/2014 | Review |
dr_459 | atomic_load missing const qualifier | 10/2014 | Review |
dr_460 | aligned_alloc underspecified | 10/2014 | Review |
dr_461 | problems with references to objects in signal handlers | 10/2014 | Open |
dr_462 | Clarifying objects accessed in signal handlers | 10/2014 | Open |
dr_463 | Left-shifting into the sign bit | 10/2014 | Review |
dr_464 | Clarifying the Behavior of the #line Directive | 10/2014 | Open |
dr_465 | Fixing an inconsistency in atomic_is_lock_free | 10/2014 | Open |
dr_466 | scope of a for loop control declaration | 10/2014 | Open |
dr_467 | maximum representable finite description vs math | 10/2014 | Open |
dr_468 | strncpy_s clobbers buffer past null | 10/2014 | Open |
dr_469 | lock ownership vs. thread termination | 10/2014 | Open |
dr_470 | mtx_trylock should be allowed to fail spuriously | 10/2014 | Open |
dr_471 | Complex math functions cacosh and ctanh | 10/2014 | Open |
Submitter: Nick Stoughton (US)
Submission Date: 2011-10-24
Source: Austin Group
Reference Document: N1559
Subject: realloc
with size zero
problems
Summary
There are at least three existing realloc
behaviors when NULL
is returned; the differences
only occur for a size of 0 (for non-zero size, all three
implementations set errno
to ENOMEM
when returning NULL
, even though that is not
required by C99).
realloc(NULL,0)
always returns NULL, errno is
EINVALrealloc(ptr,0)
always returns NULL, ptr freed,
errno is EINVALrealloc(NULL,0)
only gives NULL on alloc
failure, errno is ENOMEMrealloc(ptr,0)
only gives NULL on alloc failure,
ptr unchanged, errno is ENOMEMrealloc(NULL,0)
only gives NULL on alloc
failure, errno is ENOMEMrealloc(ptr,0)
always returns NULL, ptr freed,
errno unchangedThe Austin Group raised this matter with WG14 during earlier meetings (most notably during the London 2011 meeting). The direction from WG14 has led to The Austin Group aligning the POSIX text more closely to that in C99 and C1x as a part of TC1.
The behavior now required in POSIX is that implemented by BSD above. However, C99 has a loophole in implementation-defined behavior that still appears to permit AIX and glibc behaviors. The C1x draft carries the same wording loophole, so the planned course of action is to raise a defect against C1x once it completes standardization, where the outcome of that defect will either be that C1x tightens the wording to eliminate the loophole or relaxes the wording to align with existing practice. Therefore, the behavior of errno in Issue 8 should be deferred until after any C1x defect has been resolved.
If the size of the space requested is zero, the behavior is implementation-defined: either a null pointer is returned, or the behavior is as if the size were some nonzero value, except that the returned pointer shall not be used to access an object.
An implementation should not be allowed to define the behavior of returning a null pointer as a successful reallocation; if a null pointer is returned, then the orignal pointer has not been freed.
Suggested Change
At 7.22.3, para 1, change:If the size of the space requested is zero, the behavior is implementation-defined: either a null pointer is returned, or the behavior is as if the size were some nonzero value, except that the returned pointer shall not be used to access an object.to
If the size of the space requested is zero, the behavior is implementation-defined: either a null pointer is returned and errno set to indicate the error, or the behavior is as if the size were some nonzero value, except that the returned pointer shall not be used to access an object.Add a footnote to this sentence stating:
Note Memory allocated by these functions should be freed via a call tofree
, and not by means of arealoc(p, 0)
.
Committee Discussion
Feb 2012 meeting
- It was pointed out that the AIX behavior was not correct according to Rajan, in fact the behavior is that errno is not set.
- This change came out of a defect report on C99
Committee Discussion
- The current C Rationale needs to be edited.
Proposed Technical Corrigendum
In subsection 7.22.3 paragraph 1, changeIf the size of the space requested is zero, the behavior is implementation-defined: either a null pointer is returned, ...toIf the size of the space requested is zero, the behavior is implementation-defined: either a null pointer is returned to indicate an error, ...In subsection 7.22.3.5 (Therealloc
function), change the final sentence of paragraph 3 fromIf memory for the new object cannot be allocated, the old object is not deallocated and its value is unchanged.toIfIn subsection 7.22.3.5 (Thesize
is non-zero and memory for the new object is not allocated, the old object is not deallocated. Ifsize
is zero and memory for the new object is not allocated, it is implementation-defined whether the old object is deallocated. If the old object is not deallocated, its value shall be unchanged.realloc
function), change paragraph 4 fromThetorealloc
function returns a pointer to the new object (which may have the same value as a pointer to the old object), or a null pointer if the new object could not be allocated.TheAdd to subsection 7.31.12 a new paragraph (paragraph 2):realloc
function returns a pointer to the new object (which may have the same value as a pointer to the old object), or a null pointer if the new object has not been allocated.Invokingrealloc
with a size argument equal to zero is an obsolescent feature.
Submitter: Batty
Submission Date: 2011-10-14
Source: WG 14
Reference Document: N1584
Subject: "happens before" can not be cyclic
Summary
C++11 forbids "happens before" from being cyclic, but this change has not made its way into C11. In order to fix this, the following sentence (taken from C++ N3291, 1.10p12) should be added to 5.1.2.4p18:
The implementation shall ensure that no program execution demonstrates a cycle in the "happens before" relation.
NOTE: This cycle would otherwise be possible only through the use of consume operations.
Suggested Technical Corrigendum
See above.
Committee Discussion
Feb 2012 meeting
- Seems as if C++ made this change at the last minute and WG 14 had already voted a document for ballot.
- There seems to be consensus to make a change along this line.
Proposed Technical Corrigendum
Add to 5.1.2.4p18:The implementation shall ensure that no program execution demonstrates a cycle in the "happens before" relation.
NOTE: This cycle would otherwise be possible only through the use of consume operations.
Submitter: Batty
Submission Date: 2011-10-14
Source: WG 14
Reference Document: N1584
Subject: memory model coherence is not aligned with
C++11
Summary
The memory model described in N1569 matches an older version of the C++0x memory model, one that allowed executions that were not intended by the designers. The recommandation is to match the C++11 text by removing the sentence starting 'Furthermore' in 5.1.2.4p22, and including the following paragraphs in section 5.1.2.4 (Taken from C++ N3291, 1.10p15 through 18):If an operation A that modifies an atomic object M happens before an operation B that modifies M , then A shall be earlier than B in the modification order of M .NOTE: The requirement above is known as write-write coherence.
If a value computation A of an atomic object M happens before a value computation B of M , and A takes its value from a side effect X on M, then the value computed by B shall either be the value stored by X or the value stored by a side effect Y on M, where Y follows X in the modification order of M.NOTE: The requirement above is known as read-read coherence.
If a value computation A of an atomic object M happens before an operation B on M, then A shall take its value from a side effect X on M, where X precedes B in the modification order of M.NOTE: The requirement above is known as read-write coherence.
If a side effect X on an atomic object M happens before a value computation B of M, then the evaluation B shall take its value from X or from a side effect Y that follows X in the modification order of M.NOTE: The requirement above is known as write-read coherence.
Suggested Technical Corrigendum
See above.Committee Discussion
Feb 2012 meeting
- The consensus was that this is an oversight, and should be changed as recommended.
Proposed Technical Corrigendum
In 5.1.2.4 Paragraph 22 starting at the third sentence, add:If an operation A that modifies an atomic object M happens before an operation B that modifies M , then A shall be earlier than B in the modification order of M .NOTE: The requirement above is known as write-write coherence.
If a value computation A of an atomic object M happens before a value computation B of M , and A takes its value from a side effect X on M, then the value computed by B shall either be the value stored by X or the value stored by a side effect Y on M, where Y follows X in the modification order of M.NOTE: The requirement above is known as read-read coherence.
If a value computation A of an atomic object M happens before an operation B on M, then A shall take its value from a side effect X on M, where X precedes B in the modification order of M.NOTE: The requirement above is known as read-write coherence.
If a side effect X on an atomic object M happens before a value computation B of M, then the evaluation B shall take its value from X or from a side effect Y that follows X in the modification order of M.NOTE: The requirement above is known as write-read coherence.
Submitter: Batty
Submission Date: 2011-10-14
Source: WG 14
Reference Document: N1584
Subject: malloc() and free() in the
memory model
Summary
The synchronisation afforded to malloc and free is missing some vital ordering, and as the definition stands it makes little sense and creates cycles in happens before. C++11 includes a total order over the allocation and deallocation calls, which fixes the problem and seems to give a sensible semantics. From 18.6.1.4p1 in N3291:Calls to these functions that allocate or deallocate a particular unit of storage shall occur in a single total order, and each such deallocation call shall happen before the next allocation (if any) in this order.Unfortunately, there is a typo here. Happens before edges are not transitively closed in to the happens before relation, but the edges here are meant to be. Instead the sentence above should create a synchronises with edge. In light of this, we suggest removing the last two sentences from 7.22.3p2 and replacing them with:
Calls to these functions that allocate or deallocate a particular region of memory shall occur in a single total order, and each such deallocation call shall synchronise with the next allocation (if any) in this order.
Suggested Technical Corrigendum
See above.Committee discussion
Feb 2012 meeting
- The consensus was that this is an oversight, and should be changed along the lines that are recommended.
Proposed Technical Corrigendum
Replace last two sentences in 7.22.3 paragraph 2 with:Calls to these functions that allocate or deallocate a particular region of memory shall occur in a single total order, and each such deallocation call shall synchronize with the next allocation (if any) in this order.
Submitter: Batty
Submission Date: 2011-10-14
Source: WG 14
Reference Document: N1584
Subject: joke fragment remains in a footnote
Summary
C11 seems to have inherited part of a joke from C++, which ought to be removed or made whole and annotated as such. Originally, C++0x had the footnotes:"Atomic objects are neither active nor radioactive" and "Among other implications, atomic variables shall not decay".The first is pretty clearly a joke, but it's not obvious that the second doesn't have some technical meaning, and that is the one that remains in C11 in 7.17.3p13.
Suggested Technical Corrigendum
See above.Committee discussion
Feb 2012
- It is not clear that rewording will make the footnote useful.
Proposed Technical Corrigendum
In 7.17.3 Paragraph 13, remove footnote 256.
Submitter: Batty
Submission Date: 2011-10-14
Source: WG 14
Reference Document: N1584
Subject: The mutex specification
Summary
The C11 specification of mutexes is missing the total order over all the calls on a particular mutex. This is present in C++11. The following is from 30.4.1.2p5 in N3291:For purposes of determining the existence of a data race, these behave as atomic operations (1.10). The lock and unlock operations on a single mutex shall appear to occur in a single total order. [ Note: this can be viewed as the modification order (1.10) of the mutex. — end note ]The synchronisation in 7.26.4 is defined in terms of some order over these calls, even though none is specified, for instance 7.26.4.4p2 reads:
Prior calls to mtx_unlock on the same mutex shall synchronize with this operation.This seems like simple omission. We suggest adding a new paragraph to 7.26.4 that matches C++11:
For purposes of determining the existence of a data race, mutex calls behave as atomic operations. The lock and unlock operations on a single mutex shall appear to occur in a single total order.NOTE: This can be viewed as the modification order of the mutex.
Suggested Technical Corrigendum
See above.Committee discussion
Feb 2012 meeting
- The quoted text was added to C++11 after WG 14 voted out the FDIS in London.
- The consensus was that this is probably an oversight, and should be changed along the lines that are recommended above.
Committee Discussion
Oct 2012 meeting
- The wording in the proposed wording is not correct, should read "shall occur" rather than "shall appear to occur."
- No consensus reached on the words, Clark Nelson took action item to furnish words for the next meeting.
Committee Discussion
Add the following as 7.26.4 p1 and p2:For purposes of determining the existence of a data race, lock and unlock operations behave as atomic operations. All lock and unlock operations on a particular mutex occur in some particular total order.Apr 2013 meetingNOTE: This total order can be viewed as the modification order of the mutex.
Committee Discussion
Accept wording from Oct 2012 as proposed technical corrigendumProposed Technical Corrigendum
Add the following as 7.26.4 p1 and p2:For purposes of determining the existence of a data race, lock and unlock operations behave as atomic operations. All lock and unlock operations on a particular mutex occur in some particular total order.NOTE: This total order can be viewed as the modification order of the mutex.
Submitter: Batty
Submission Date: 2011-10-14
Source: WG 14
Reference Document: N1584
Subject: Visible sequences of side effects are
redundant
Summary
It has been mathematically proved that a simplification can be made to the memory model as it is specified in the final draft of the C++11 standard. Essentially, the restriction defining visible sequence of side effects (vsse) is redundant and can be removed with no ill effects. The main motivation for doing this is that the current restriction is misleading. 5.1.2.4p22 defines vsse's:The visible sequence of side effects on an atomic object M, with respect to a value computation B of M, is a maximal contiguous sub-sequence of side effects in the modification order of M, where the first side effect is visible with respect to B, and for every subsequent side effect, it is not the case that B happens before it. The value of an atomic object M, as determined by evaluation B, shall be the value stored by some operation in the visible sequence of M with respect to B.The wording of this paragraph makes it seem as if the vsse identifies the writes that an atomic read is allowed to read from, but this is not the case. There can be writes in the vsse that cannot be read due to the coherence requirements (to be included in C, 1.10p15 through 1.10p18 in C++ N3291). Consequently this is even more confusing than it at first appears.
Also propose changing 5.1.2.4p22 to the following:
The value of an atomic object M, as determined by evaluation B, shall be the value stored by some side effect A that modifies M, where B does not happen before A.With a note to remind the reader of the coherence requirements:
NOTE: The set of side effects that a given evaluation might take its value from is also restricted by the rest of the rules described here, and in particular, by the coherence requirements belowIf the committee is concerned about allowing a differing text from C++11, then a note could be added to assure the reader:
NOTE: Although the rules for multi-threaded executions differ here from those of C++11, the executions they allow are precisely the same. Visible sequences of side effects are a redundant restriction.
Suggested Technical Corrigendum
See above.Committee Discussion
Feb 2012 meeting
- The changes seem reasonable, there is a concern about having C and C++ differ.
- Should be contingent on Defect 402.
Committee Discussion
Oct 2012 meeting
- There was no work done on this defect between meetings.
- Seems more complicated than thought at the last meeting.
- It is not clear if WG 21 has considered these changes.
- Clark Nelson took an action item to liaise with WG 21.
Committee Discussion
This item has become WG21 Core issue 1466Apr 2013 meeting
Committee Discussion
There has been no discussion or action from WG21.Oct 2013 meeting
Committee Discussion
These changes have been proposed for the C++ working draft:
Change 1.10 paragraph 14 as follows:
The visible sequence of side effects on an atomic object M, with respect to a value computation B of M, is a maximal contiguous sub-sequence of side effects in the modification order of M, where the first side effect is visible with respect to B, and for every side effect, it is not the case that B happens before it.The value of an atomic object M, as determined by evaluation B, shall be the value stored by someoperation in the visible sequence of M with respect to Bside effect A that modifies M, where B does not happen before A. [Note:It can be shown that the visible sequence of side effects of a value computation is unique givenThe set of side effects that a given evaluation might take its value from is also restricted by the rest of the rules described here, and in particular, by the coherence requirements below. —end note]
1.10p20 should be changed as follows:
[ Note: Thevisible sequence of side effectsvalue observed by a load of an atomic depends on the "happens before" relation, which depends on the values observed by loads of atomics, which we are restricting here. The intended reading is that there must exist an association of atomic loads with modifications they observe that, together with suitably chosen modification orders and the "happens before" relation derived as described above, satisfy the resulting constraints as imposed here. —end note ]
I think 1.10p22 should be changed as follows:
[ Note: Compiler transformations that introduce assignments to a potentially shared memory location that would not be modified by the abstract machine are generally precluded by this standard, since such an assignment might overwrite another assignment by a different thread in cases in which an abstract machine execution would not have encountered a data race. This includes implementations of data member assignment that overwrite adjacent members in separate memory locations. Reordering of atomic loads in cases in which the atomics in question may alias is also generally precluded, since this may violate the"visible sequence"coherence rules. —end note ]
I believe the 29.3p3 wording should change as follows:
- the result of the last modification A of M that precedes B in S, if it exists, or
- if A exists, the result of some modification of M
in the visible sequence of side effects with respect to Bthat is notmemory_order_seq_cst
and that does not happen before A, or- if A does not exist, the result of some modification of M
in the visible sequence of side effects with respect to Bthat is notmemory_order_seq_cst
.
Apr 2014 meeting
Committee Discussion
WG21 liaison has been asked to ascertain status of this w.r.t. C++14 and to provide a suggested TC.
Oct 2014 meeting
Committee Discussion
A paper N1856 was provided that discusses the drift between the two Standards and a first cut at some possible wording changes, as follows. It was not, however, discussed, but does provide insight as to the necessary direction for a resolution to this DR.
Change 5.1.2.4 paragraph 22 as follows:
The visible sequence of side effects on an atomic object M, with respect to a value computation B of M, is a maximal contiguous sub-sequence of side effects in the modification order of M, where the first side effect is visible with respect to B, and for every side effect, it is not the case that B happens before it.The value of an atomic object M, as determined by evaluation B, shall be the value stored by someoperation in the visible sequence of M with respect to Bside effect A that modifies M, where B does not happen before A. [Note:It can be shown that the visible sequence of side effects of a value computation is unique givenThe set of side effects that a given evaluation might take its value from is also restricted by the rest of the rules described here, and in particular, by the coherence requirements below. —end note]
Change 5.1.2.4 paragraph 24 as follows:
[Note: The
visible sequence of side effectsvalue observed by a load of an atomic depends on the “happens before” relation, which depends on the values observed by loads of atomics, which we are restricting here. The intended reading is that there must exist an association of atomic loads with modifications they observe that, together with suitably chosen modification orders and the “happens before” relation derived as described above, satisfy the resulting constraints as imposed here. —end note]
Change 5.1.2.4 paragraph 27 as follows:
[Note: Compiler transformations that introduce assignments to a potentially shared memory location that would not be modified by the abstract machine are generally precluded by this standard, since such an assignment might overwrite another assignment by a different thread in cases in which an abstract machine execution would not have encountered a data race. This includes implementations of data member assignment that overwrite adjacent members in separate memory locations. Reordering of atomic loads in cases in which the atomics in question may alias is also generally precluded, since this may violate the
“visible sequence”coherence rules. —end note]
Change 7.17.3 paragraph 6 as follows:
There shall be a single total order S on all memory_order_seq_cst operations, consistent with the “happens before” order and modification orders for all affected locations, such that each memory_order_seq_cst operation B that loads a value from an atomic object M observes one of the following values:
the result of the last modification A of M that precedes B in S, if it exists, or
if A exists, the result of some modification of M
in the visible sequence of side effects with respect to Bthat is not memory_order_seq_cst and that does not happen before A, orif A does not exist, the result of some modification of M
in the visible sequence of side effects with respect to Bthat is not memory_order_seq_cst.[Note:...
Submitter: Batty
Submission Date: 2011-10-14
Source: WG 14
Reference Document: N1584
Subject:
Summary
C11 seems to omit the restriction imposed in C++11 in 29.3p7 (from N3291):For atomic operations A and B on an atomic object M, if there are memory_order_seq_cst fences X and Y such that Ais sequenced before X, Y is sequenced before B, and X precedes Y in S, then B occurs later than A in the modification order of M.Furthermore, it seems that both C11 and C++11 are missing the following two derivatives of this rule:
For atomic modifications A and B of an atomic object M, if there is a memory_order_seq_cst fence X such that A is sequenced before X, and X precedes B in S, then B occurs later than A in the modification order of M.
For atomic modifications A and B of an atomic object M, if there is a memory_order_seq_cst fence Y such that Y is sequenced before B, and A precedes Y in S, then B occurs later than A in the modification order of M.
Suggested Technical Corrigendum
See above.Committee Discussion
Feb 2012 meeting
- The changes are difficult to fully understand - a diagram might help.
- A paper for the next meeting would help the Committee make progress on this.
- Some concern about having C and C++ differ.
Committee Discussion
Oct 2012 meeting
- No work was done on this defect between meetings, so no additional information has been provided.
- There seems to be two issues here, and this should possibility be two defect reports rather than one.
- No known liaison has taken place with WG 21 on this issue.
- The proposed words are inappropriate for C, still need words that work for C.
- There seems to be editorial issues with the proposed words for C++ as well.
- There is consensus to adopt something along the lines of the first proposed change, with wording that is consistent with C.
- WG 14 needs to stay in step with WG 21.
- Clark Nelson took an action item to liaise with WG 21.
Committee Discussion
Oct 2013 meeting
- This item has also become WG21 Library Issue 2130.
- Rajan presented a diagram and argued that we should adopt the first part and that the second two parts were redundant.
After 7.17.3 paragraph 11 add the following:
For atomic operations A and B on an atomic object M, if there are memory_order_seq_cst fences X and Y such that A is sequenced before X, Y is sequenced before B, and X precedes Y in S, then B occurs later than A in the modification order of M.
Committee Discussion
Change 29.3 [atomics.order] paragraph 7 as indicated: [Drafting note: Note that the wording change intentionally does also replace the term atomic operation by atomic modification]
-7-
For atomic operations A and B on an atomic object M, if there are memory_order_seq_cst fences X and Y such that A is sequenced before X, Y is sequenced before B, and X precedes Y in S, then B occurs later than A in the modification order of M.For atomic modifications A and B of an atomic object M, B occurs later than A in the modification order of M if:
- there is a memory_order_seq_cst fence X such that A is sequenced before X, and X precedes B in S, or
- there is a memory_order_seq_cst fence Y such that Y is sequenced before B, and A precedes Y in S, or
- there are memory_order_seq_cst fences X and Y such that A is sequenced before X, Y is sequenced before B, and X precedes Y in S.
Committee Discussion
WG21 liaison has been asked to ascertain status of this w.r.t. C++14 and to provide a suggested TC.
Oct 12014 meeting
Committee Discussion
A paper N1856 was provided that discusses the drift between the two Standards and a first cut at some possible wording changes, as follows. It was not, however, discussed, but does provide insight as to the necessary direction for a resolution to this DR.
[Drafting note: The project editor is kindly asked to consider to replace in 1.10 [intro.multithread] p17 the phrase "before an operation B on M" by "before a modification B of M".]
Change 7.17.3 paragraph 11 as indicated: [Drafting note: Note that the wording change intentionally does also replace the term atomic operation by atomic modification]
For atomic operations A and B on an atomic object M, if
there are memory_order_seq_cst fences X and Y such that A
is sequenced before X, Y is sequenced before B, and X
precedes Y in S, then B occurs later than A in the modification
order of M. For atomic modifications A and B of an atomic
object M, B occurs later than A in the modification order of
M if:
[ Note: memory_order_seq_cst ensures sequential consistency only for a program that is free of data races and uses exclusively memory_order_seq_cst operations. Any use of weaker ordering will invalidate this guarantee unless extreme care is used. In particular, memory_order_seq_cst fences ensure a total order only for the fences themselves. Fences cannot, in general, be used to restore sequential consistency for atomic operations with weaker ordering specifications. —end note ]
Submitter: Batty
Submission Date: 2011-10-14
Source: WG 14
Reference Document: N1584
Subject: Should locks provide intra-thread
synchronization
Summary
Most of the C++ standard, synchronisation is used exclusively inter-thread, so in particular, synchronisation can't be used to avoid undefined behavior arising from conflicting un-sequenced memory accesses, e.g.:(x = 1)==(x = 2)Firstly, C does not define this sort of thing as undefined behavior. Is this intentional? Secondly in C++ locks can currently be used to fix up such programs and avoid undefined behavior, e.g.:
(lock; x = 1; unlock; x)==(lock; x = 2; unlock; x)The reason not to allow this sort of synchronisation in general is, because it disallows some single threaded compiler optimisations. Is intra-thread locking intended to be defined and usable?
Suggested Technical Corrigendum
Committee Discussion
Feb 2012 meeting
- The changes seem reasonable, but without actual text no position can be formed.
- A paper for the next meeting is probably the best way to make progress.
Committee Discussion
Oct 2012 meeting
- Blaine Garst and the submitter worked on this between meetings, they believe this is not a problem.
- Convener prefers this remain OPEN for now, there could be additional text as discussed in Oct.
- C11 does define the semantics of a lock within a single thread.
Proposed Committee Response
- The expression
(x = 1)==(x = 2)
has undefined behavior in C11.- The happens-before relationship imposed by
(lock(), x = 1, unlock())
does not constrain the possible interwoven order of evaluation of(lock(), x = 2, unlock())
.
Submitter: Fred J. Tydeman (USA)
Submission Date: 2012-1-11
Source: WG 14
Reference Document: N1593
Subject: f(inf)
is inf
being a
range error
Summary
Several of the functions in <math.h>
that
compute infinity for f(infinity)
have the phrase (or
something similar):
A range error occurs if the magnitude of x is too large.
Since infinity is 'too large', one might conclude that
f(infinity)
is a range error for those
functions.
However, 7.12.1#5 has:
A floating result overflows if the magnitude of the mathematical result is finite but so large that the mathematical result cannot be represented without extraordinary roundoff error ...
The key word being 'finite'. So, one could conclude f(infinity) being infinity is not overflow (and therefore, not a range error).
To me, this appears to be a contradiction. I have encountered both kinds of implementations; some treat this case as a range error, and others that do not.
For both LIA and IEEE-754, f(infinity) being infinity is not considered an error.
Suggested Change
Change to: A range error occurs if the magnitude of finite x is too large.
Change to: A range error occurs if the magnitude of finite x is too large.
Change to: A range error occurs if the magnitude of finite x is too large.
Change to: A range error occurs if the magnitude of finite x is too large.
Change to: A range error occurs if the magnitude of finite x is too large.
Change to: A range error may occur for finite arguments.
Change to: A range error may occur for finite arguments.
Change to: A range error may occur for finite arguments.
Change to: A range error may occur for finite arguments.
Change to: A range error occurs if finite x is too large.
Change to: A range error occurs if finite x is too large.
Change to: A range error occurs if the magnitude of finite x is too large and may occur if the magnitude of x is too small.
Change to: A range error may occur for finite arguments.
Change to: A range error may occur for finite arguments.
Committee Discussion
Oct 2012 meeting
- The committee rejected the Suggested Change in the main body of this defect report.
- The committee considered the following, but rejected it (as just being a restatement of 7.12.1 paragraphs 4 and 5).
If the result overflows, a range error shall occur.- A question arose as to why these range error cases are listed in the individual functions (instead of just being covered by the blanket 7.12.1 paragraphs 4, 5, and 6)
7.12.1 paragraph 1 has the answer:
The behavior of each of the functions in <math.h> is specified for all representable values of its input arguments, except where stated otherwise.- Several other approaches were discussed, without any consensus reached
- Add a footnote to 7.12.1 paragraph 5, first sentence:
In an implementation that supports infinities, a range error may happen for functions that map an infinity argument into an exact infinity or exact zero result.- Add to end of 7.12.1 paragraph 4:
Recommended practiceIn an implementation that supports infinities, a range error should not happen for functions that map an infinity argument into an exact infinity or exact zero result.
- Add to 7.12.1 paragraph 4:
An implementation may define additional range errors, provided that such errors are consistent with the mathematical definition of the function.
Committee Discussion
- Fred wrote a paper N1629 discussing "Fixing a contradiction" and "Taking care of infinity".
- The definition of range error, however, in 7.12.1 paragraph 4 indicates infinity is excluded (since it has a representation), and as such no change is required.
Proposed Committee Response
The definition of range error in 7.12.1 paragraph 4 excludes infinity.
For example, exp(+infinity)
is +infinity
.
Since the input +infinity
is representable,
then the output +infinity
is representable in an object of the
specified type. By, 7.12.1 paragraph 4, a range error has not happened.
Also, by 7.12.1 paragraph 5, since the result is not finite,
a range error has not happened.
Submitter: Fred J. Tydeman (USA)
Submission Date: 2012-1-11
Source: WG 14
Reference Document: N1595
Subject: ilogb
inconsistent with
lrint
, lround
For the case of converting a large finite value to an integer value, lrint and lround have one set of requirements, while ilogb has another set. This is inconsistent.
Both 7.12.9.5 The lrint and llrint functions and 7.12.9.7 The lround and llround functions have:
If the rounded value is outside the range of the return type, the numeric result is unspecified and a domain error or range error may occur.
While 7.12.6.5 The ilogb functions has:
If the correct value is outside the range of the return type, the numeric result is unspecified.
I believe that the following changes to C11 should be done.
Change to: If the correct value is outside the range of the return type, the numeric result is unspecified and a domain error or range error may occur.
Committee discussion
- Some believe the rationale presented as a reason for doing this is not a valid rationale for the change.
Proposed Technical Corrigendum
In 7.12.6.5 paragraph 2, change the last sentence to:If the correct value is outside the range of the return type, the numeric result is unspecified and a domain error or range error may occur.
Submitter: Project Editor (Larry Jones)
Submission Date: 2012-01-18
Source: Project Editor
Reference Document: N/A
Subject: Predefined macro values
Summary
The actual values for the predefined macros __STDC_VERSION__ and __STDC_LIB_EXT1__ should be specified.
Suggested Technical Corrigendum
Change the relevant list entry in 6.10.8.1 to:
__STDC_VERSION__ The integer constant 201112L.
Change the relevant list entry in 6.10.8.3 to:
__STDC_LIB_EXT1__ The integer constant 201112L.
Committee Discussion
- The committee asked the Convener to look into making this an errata if possible.
Proposed Technical Corrigendum
Change 6.10.8.1 from:
__STDC_VERSION__ The integer constant201ymmL.178)to:__STDC_VERSION__ The integer constant 201112L.178)Change 6.10.8.3 from:
__STDC_LIB_EXT1__ The integer constant201ymmL, intended to indicate support for the extensions defined in annex K (Bounds-checking interfaces).179)to:__STDC_LIB_EXT1__ The integer constant 201112L, intended to indicate support for the extensions defined in annex K (Bounds-checking interfaces).179)
Submitter: Edward Diener (comp.std.c)
Submission Date: 2012-01-18
Source: Project Editor (Larry Jones)
Reference Document: N/A
Subject: #elif
Summary
It appears that #elif is not entirely equivalent to #else, #if, and #endif.
Consider the code:
#if 1
...
#else
#if this is not a valid expression
...
#endif
#endif
This is well-defined. Since the controlling expression of the #if evaluates to true, the #else group is skipped and thus the nested #if is only processed through the directive name (6.10.1p6).
However, if this is recast using #elif:
#if 1
...
#elif this is not a valid expression
...
#endif
the #elif is not part of a group that is skipped and thus must be processed completely, including evaluating the controlling condition (even though the resulting value is of no interest).
I do not believe this was the committee's intent.
Suggested Technical Corrigendum
In 6.10.1p6, change:
Only the first group whose control condition evaluates to true (nonzero) is processed.
to:
Only the first group whose control condition evaluates to true (nonzero) is processed; any following groups are skipped and their controlling directives are processed as if they were in a group that is skipped.
Proposed Technical Corrigendum
In 6.10.1p6, change:
Only the first group whose control condition evaluates to true (nonzero) is processed.
to:
Only the first group whose control condition evaluates to true (nonzero) is processed; any following groups are skipped and their controlling directives are processed as if they were in a group that is skipped.
Submitter: Willem Wakker
Submission Date: 2012-01-27
Source: WG14
Reference Document:
N1601
Subject: initialization
Summary
Consider the following code: typedef struct {
int k;
int l;
int a[2];
} T;
typedef struct {
int i;
T t;
} S;
T x = {.l = 43, .k = 42, .a[1] = 19, .a[0] = 18 };
void f(void)
{
S l = { 1, .t = x, .t.l = 41, .t.a[1] = 17};
}
The question is: what is now the value of l.t.k
? Is it
42 (due to the initialization of .t = x
) or is it 0 (due
to the fact that .t.l
starts an incomplete initialization
of .t
?
19 The initialization shall occur in initializer list order, each initializer provided for a particular subobject overriding any previously listed initializer for the same subobject;151) all subobjects that are not initialized explicitly shall be initialized implicitly the same as objects that have static storage duration.
Suggested Technical Corrigendum
Committee Discussion
Oct 2012 meeting
- It was noted that this is basically similar to dr_253.
- The following was proposed, but there was no consensus for adoption.
The initialization shall occur in initializer list order, each initializer provided for a particular subobject overriding any previously listed initializer for the same subobject 151). Subsequently, all subobjects that are not initialized explicitly previously shall be initialized implicitly the same as objects that have static storage duration.
Committee Discussion
Apr 2013 meeting
- The original author intended the result to be 42 by the following reasoning:
- 6.7.9 paragraphs 17-18 specify that each designator list affects only the smallest subobject to which the designator list refers. As a result, the second clause of paragraph 19 occurs once for the greater object as a whole, filling in only those parts of the whole object that were never initialized explicitly.
- gcc and some IBM compilers give the result as 0, although it is not believed that there is code dependent on this interpretation.
- David Keaton proposed N1659.
- This, however, does not clarify the interpreted conflict of paragraph 19 "subobjects that are not initialized explicitly [shall be set to zero]" applied "recursively to subaggregates" (paragraph 20).
- Adding the example is a desired outcome.
Committee Discussion
Oct 2013 meetingThere was no work performed on this DR. Although both GCC and six compilers from IBM provide the unintended answer, it is believed to be such a rarely used feature that it is not depended upon to a great degree, and the compiler venders are willing to change their behavior appropriately.
Committee Discussion
There has been considerable discussion and several proposals ( N1659, N1749) to clarify the standard to no avail. Upon reflection, and consultation with the author, we believe that the proper course of action is twofold. First, simply answer the question asked as the committee believes that the standard already specifies correctly. To add clarification to the standard we will also add the examples from N1659 that leads to this answer.
Proposed Committee Response
The proper answer to the question raised according to the standard is that the value of l.t.k is 42, because implicit initialization does not override explicit initialization. We will also provide a non-normative example to further clarify the intent.Proposed Committee Corrigendum
Add the following example to 6.7.9:
typedef struct {
int k;
int l;
int a[2];
} T;
typedef struct {
int i;
T t;
} S;
T x = {.l = 43, .k = 42, .a[1] = 19, .a[0]
= 18 };
void f(void)
{
S l = { 1, .t = x, .t.l = 41,
.t.a[1] = 17};
}
The value of l.t.k is 42, because implicit initialization does not override explicit initialization.
Submitter: Tom Plum
Submission Date: 2012-03-20
Source: WG14
Reference Document:
N1614
Subject: Typos in 6.27 Threads <threads.h>
The enumeration constants arethe "test and return" is referring towhich is passed tomtx_plain
mtx_init
to create a mutex object that supports neither timeout nor test and return;
try_lock
, try_lock
is not optional, therefore the "test and return" should be removed.
TheThere are not six values listed, "six" should be changed to "these".mtx_init
function creates a mutex object with properties indicated bytype
,
which must have one of the six values:
mtx_plain
for a simple non-recursive mutex,
mtx_timed
for a non-recursive mutex that supports timeout,
mtx_plain | mtx_recursive
for a simple recursive mutex, or
mtx_timed | mtx_recursive
for a recursive mutex that supports timeout.
Suggested Technical Corrigendum
The enumeration constants areChange 7.26.4.2 paragraph 2 towhich is passed tomtx_plain
mtx_init
to create a mutex object that does not support timeout;
Themtx_init
function creates a mutex object with properties indicated bytype
,
which must have one of the these values:
Committee Discussion
Adopt the Suggested Technical Corregendum as proposed.
Proposed Technical Corrigendum
The enumeration constants areChange 7.26.4.2 paragraph 2 towhich is passed tomtx_plain
mtx_init
to create a mutex object that does not support timeout;
Themtx_init
function creates a mutex object with properties indicated bytype
,
which must have one of the these values:
Submitter: Seacord (PL22.11)
Submission Date: 2012-06-02
Source: WG14
Reference Document:
N1617
Subject: Missing divide by zero entry in Annex J
Summary
The undefined behavior defined in paragraph 6 of 6.5.5 is missing in J.2 and should be added.
Suggested Technical Corrigendum
Add a bullet with the following text to J.2 after bullet 45
If the quotienta/b
is not representable, the behavior of botha/b
anda%b
is undefined (6.5.5).
Committee Discussion
Adopt the Suggested Technical Corregendum as proposed.
Proposed Technical Corrigendum
Add a bullet with the following text to J.2 after bullet 45
If the quotienta/b
is not representable, the behavior of botha/b
anda%b
is undefined (6.5.5).
Submitter: Owen Shepherd
Submission Date: 2012-08-12
Source: WG14
Reference Document:
N1627
Subject: tss_t
destruction unspecified
The standard does not specify if or when destructors for
thread specific data keys (created with the tss_create
function) are invoked.
This proposal suggests to align the behavior with that
specified by POSIX for pthread_key_t
.
This behavior is hoped to both match the intention of
the standard, and be possible to implement on other systems (it is
noted that a pthreads implementation exists for Windows, for example,
while the behavior of POSIX and Windows thread local storage
implementations differ greatly)
After 7.26.5.1p2, add
Returning from func
shall have the same behaviour as invoking thrd_exit
with the returned value
Change 7.26.5.5 parts 2 and 3 from
The thrd_exit
function
terminates execution of the calling thread and sets its
result code to res
.
The program shall terminate normally after the last thread has been terminated. The behavior shall be as if the program called the exit function with the status EXIT_SUCCESS at thread termination time.
to
For every thread specific storage key
which was created with a non-NULL destructor and for which the value is
non-NULL, thrd_exit
shall set the value associated with
the key to NULL and then invoke the destructor with its previous value.
The order in which destructors are invoked is unspecified.
If after this process there remain keys
with both non-NULL destructors and values, the implementation shall
repeat this process up to TSS_DTOR_ITERATIONS
times.
If thrd_exit
is not
being invoked in the last thread in the process, then the
implementation shall terminate execution of the calling thread and
set its result code to res
. Otherwise, the
implementation shall behave as if exit(EXIT_SUCCESS)
was invoked.
(This change provides application writers guarantees about the
identity of the thread executing functions registered with atexit
)
After 7.26.6.1p2, add
The value NULL shall be associated with the newly created key in all existing threads. Upon thread creation, the value associated with all keys shall be initialized to NULL
Note that destructors associated thread specific storage are not invoked at process exit.
To 7.26.6.2p2, append
If tss_delete
is called
while another thread is executing destructors, whether this will affect
the number of invocations of the destructor associated with key
on that thread is unspecified. If the thread from which tss_delete
is invoked is executing destructors, then no further invocations of the
destructor associated with key
will occur on said
thread.
Calling tss_delete
will not result in the invocation of any destructors.
After 7.26.6.4p2, add
This action will not invoke the destructor associated with the key on the value being replaced.
Committee Discussion
Apr 2013 meeting
- This paper was discussed in conjunction with DR424 which covers one additional related issue.
- The key observation is that these papers are about the deliberate underspecification of threads, since that allows the greatest opportunity for implementation on a variety of operating systems.
- Pete Becker, an implementor of the C11 threads library, was asked about these papers and replied in SC22/WG14 message 12813.
- Based on that response the committee is concerned that there could be subtleties in adopting the Proposed Technical Corrigendum and that, as such, these changes are substantial enough to warrant a proposal and to not be considered a defect.
Committee Discussion
Oct 2013 meeting
- A revised paper N1687 was submitted, although not in the recommended html format.
- The suggested technical corrigendum would specify new instances of undefined behavior as well as new requirements on implementations.
- The committee expressed concern that some of the new requirements may be onerous or impossible to provide above the native platform implementation.
Committee Discussion
After several papers N1750, N1751, revisions, and much discussion, the committee has agreed on the following as the resolution.
Proposed Technical Corrigendum
After 7.26.5.1 paragraph 2, add
Returning from func shall have the same behavior as invoking thrd_exit with the value returned from func.Change 7.26.5.5, replace paragraph 2 with:
For every thread-specific storage key which was created with a non-null destructor and for which the value is non-null, thrd_exit shall set the value associated with the key to NULL and then invoke the destructor with its previous value. The order in which destructors are invoked is unspecified.After 7.26.6.1 paragraph 2, add the following new paragraphs:If after this process there remain keys with both non-null destructors and values, the implementation shall repeat this process up to TSS_DTOR_ITERATIONS times.
Following this, the thrd_exit function terminates execution of the calling thread and sets its result code to res.
The value NULL shall be associated with the newly created key in all existing threads. Upon thread creation, the value associated with all keys shall be initialized to NULL.In 7.26.6.2 paragraph 2, add the following new second sentence:Destructors associated with thread-specific storage are not invoked at program termination.
A call to tss_create from within a destructor results in undefined behavior.
A call to tss_delete function results in undefined behavior if the call to tss_create which set key completed after the thread commenced executing destructors.After 7.26.6.2 paragraph 2, add the following new paragraphs:
If tss_delete is called while another thread is executing destructors, whether this will affect the number of invocations of the destructor associated with key on that thread is unspecified.In 7.26.6.3 paragraph 2, add the following new second sentence:Calling tss_delete will not result in the invocation of any destructors.
A call to tss_get function results in undefined behavior if the call to tss_create which set key completed after the thread commenced executing destructors.
In 7.26.6.4 paragraph 2, add the following new second sentence:
This action will not invoke the destructor associated with the key on the value being replaced.
Submitter: John Benito
Submission Date: Oct 2012
Source: WG14
Reference Document:
N1628
Subject: Annex J not updated with necessary aligned_alloc
entries
Summary
The following unspecified behaviors are incomplete in Annex J.1,aligned_alloc()
is missing in both entries.
— The order and contiguity of storage allocated by successive calls to theThe following undefined behavior bullet is incomplete in Annex J.2,calloc
,malloc
, andrealloc
functions (7.22.3).
— The amount of storage allocated by a successful call to thecalloc
,malloc
, orrealloc
function when 0 bytes was requested (7.22.3).
aligned_alloc()
is missing.
— A non-null pointer returned by a call to theThe following implementation-defined behavior bullet is incomplete in Annex J.3.12,calloc
,malloc
, orrealloc
function with a zero requested size is used to access an object (7.22.3).
aligned_alloc()
is missing.
— Whether thecalloc
,malloc
, andrealloc
functions return a null pointer or a pointer to an allocated object when the size requested is zero (7.22.3).
Suggested Technical Corrigendum
Change bullet 42 of J.1 to:— The order and contiguity of storage allocated by successive calls to theChange bullet 43 of J.1 to:calloc
,malloc
,realloc
, andaligned_alloc
functions (7.22.3).
— The amount of storage allocated by a successful call to theChange bullet 166 of J.2 tocalloc
,malloc
,realloc
, oraligned_alloc
function when 0 bytes was requested (7.22.3).
— A non-null pointer returned by a call to theChange bullet 37 of J.3.12 tocalloc
,malloc
,realloc
, oraligned_alloc
function with a zero requested size is used to access an object (7.22.3).
— Whether thecalloc
,malloc
,realloc
andaligned_alloc
functions return a null pointer or a pointer to an allocated object when the size requested is zero (7.22.3).
Committee Discussion
Adopt as proposed the Suggested Technical Corrigendum.
Proposed Technical Corrigendum
Change bullet 42 of J.1 to:— The order and contiguity of storage allocated by successive calls to theChange bullet 43 of J.1 to:calloc
,malloc
,realloc
, andaligned_alloc
functions (7.22.3).
— The amount of storage allocated by a successful call to theChange bullet 166 of J.2 tocalloc
,malloc
,realloc
, oraligned_alloc
function when 0 bytes was requested (7.22.3).
— A non-null pointer returned by a call to theChange bullet 37 of J.3.12 tocalloc
,malloc
,realloc
, oraligned_alloc
function with a zero requested size is used to access an object (7.22.3).
— Whether thecalloc
,malloc
,realloc
andaligned_alloc
functions return a null pointer or a pointer to an allocated object when the size requested is zero (7.22.3).
Submitter: Fred J. Tydeman (USA)
Submission Date: 2012-9-13
Source: WG14
Reference Document:
N1633
Related:
N1497
Subject: Possible defect report: fmod(0.,NaN) and
fmod(NaN,infinity)
First question. When Annex F is in effect, what should the value of fmod(0.,NaN) be? The two choices are 0. or NaN.
Annex F.10.7.1 The fmod functions has:
- fmod(+/-0, y) returns +/-0 for y not zero.
- fmod(x, y) returns a NaN and raises the ''invalid'' floating-point exception for x infinite or y zero (and neither is a NaN).
So, that first bullet item says fmod(0.,NaN) is 0.
Elsewhere in that annex (F.10 Mathematics, paragraph 11), we have:
Functions with a NaN argument return a NaN result and raise no floating-point exception, except where stated otherwise.
That says that fmod(0.,NaN) is NaN.
One idea is to explicitly add words about a NaN to the first bullet item in F.10.7.1, such as:
- fmod(+/-0, y) returns +/-0 for y not zero nor NaN.
However, if F.10#11 covers NaN arguments before any other arguments are considered, then words about NaN could be removed from the second case in F.10.7.1, such as:
- fmod(x, y) returns a NaN and raises the ''invalid'' floating-point exception for x infinite or y zero.
I believe that takes us back to before N1497 was done.
Second question: what should fmod(NaN,infinity) be? Must it be the same NaN argument, or may it be any NaN?
Annex F.10.7.1 The fmod functions has:
- fmod(x, +/-infinity) returns x for x not infinite.
Which says fmod(NaN,infinity) must be the same NaN argument.
But, if F.10#11 covers this NaN argument, then this case is just some NaN.
It appears that the third bullet should either be left alone or changed to:
- fmod(x, +/-infinity) returns x for finite x.
Some other functions that discuss NaN arguments in Annex F are: frexp, ilogb, modf, hypot, pow, fmax, fmin, and fma. Of those, only hypot, pow, fmax, and fmin have exceptions on NaN in implies NaN out.
Committee Discussion
- Given that, unless stated otherwise, if the argument is a NaN, the result is a NaN, and there is no floating point exception.
- The parenthetical comment might be causing confusion, and removing it seems to make it more confusing.
- Adding more explicit wording is possible but seems unnecessary.
Proposed Committee Response
The consensus was to do nothing and the author agrees.
Submitter: Douglas Walls
Submission Date: 2012-09-16
Source:WG14
Reference Document:
N1635
Subject: Generic Functions
Summary
What the heck is a "generic function", and what are the sections of the standard covering how a user (or implementor) can write a stardard conforming program defining a "type generic function"?I was trying to reconcile the rules in 7.1.4 Use of library functions allowing an implementation to define a function as a macro, and the user suppressing the macro by enclosing the name of the function in parentheses. But, I don't see how to make a function declaration, where a parameter can be any atomic type.
I've convinced myself, generic functions will take compiler magic. There is no way to declare them using C standard conforming code. Just like the type generic macros of <tgmath.h> in C99.
Somehow I missed this. I remember all the discussion of adding atomic operation to operators like += but somehow I missed the fact we were again adding in function specifications that cannot be implemented using standard C. I thought we were adding type generic macros. Sigh ...
I've been told that the discussion included talk about a proposal to recast them as generic macros, but that never happened so we ended up with generic functions through the back door without much explication.
Suggested Technical Corrigendum
Redefine the atomic type generic functions as type generic macros. Define the underlying functions to which the type generic macros expand.
Proposed Technical Corrigendum
7.17.1 add a new paragraph after paragraph 5:
It is unspecified whether any generic function declared in stdatomic.h
is a
macro or an identifier declared with external linkage. If a macro definition
is suppressed in order to access an actual function, or a program defines an
external identifier with the name of a generic function, the behavior is undefined.
J.2 add:
The macro definition of a generic function is suppressed in order to access an actual function (7.17.1)
Submitter: Jens Gustedt
Submission Date:
2012-10-08
Source: WG14
Reference Document:
N1647
Subject: sytax error in specification of for-statement
Summary
The standard lists two different forms for
the for
-statement in 6.8.5p1:
for ( expression[opt] ; expression[opt] ; expression[opt] ) statement for ( declaration expression[opt] ; expression[opt] ) statement
whereas later in 6.8.5.3 these two forms are subsumed in a third form by:
for ( clause-1 ; expression-2 ; expression-3 ) statement
Obviously the second form above is a typo and doesn't fit within this third form, the semantic that is given in 6.8.5.3 and to common practice in existing compilers.
Suggested Technical Corrigendum
Replace the second form in 6.8.5p1 and A.2.3 by the intended one:
for ( declaration expression[opt] ; expression[opt] ; expression[opt] ) statement
Proposed Committee Response
The second form of for-statement is not a typo. The syntax for "declaration", in 6.7 paragraph 1, includes an optional init-declarator-list and a trailing semicolon.
Submitter: Jens Gustedt
Submission Date:
2012-10-08
Source: WG14
Reference Document:
N1648
Subject: initialization of atomic_flag
Summary
C11 expresses the intention to have atomic_flag
as a
primitive that should allow to emulate all other atomic types and
operations, 7.17.8 p3 in a note says:
The remaining types can be emulated with atomic_flag
,
though with less than ideal properties.
With the current semantic for the initialization
of atomic_flag
this goal cannot be achieved.
Details
This is a very good concept as far as I can see, but I have one
problem to achieve this, initialization. The phrasing for atomic
types in general and for atomic_flag
are different.
For atomic_flag
we have:
An atomic_flag that is not explicitly initialized with
ATOMIC_FLAG_INIT
is initially in an indeterminate
state.
The problem is how to emulate an atomic type
with atomic_flag
during initialization. Say we emulate
with something like
struct atomic_int_struct { atomic_flag cat; int val; };
Then the ATOMIC_VAR_INIT
macro could simply look
like:
#define ATOMIC_VAR_INIT(V) { .cat = ATOMIC_FLAG_INIT, .val = (V), }
But if I’d have a variable of atomic_int_struct
with static storage duration
struct atomic_int_struct v;
is supposed to do the right thing, namely to guarantee
that v
has a valid state at startup, so it should
contain a 0
for .val
,
and .cat
must be in a determinate state. Since all
atomic operations should work without problems on v
,
the state of .cat
can’t be anything else than
“clear”.
Now looking into the possible implementations
of atomic_flag
in assembler I didn’t yet meet a
processor where the “clear” state would not be naturally
represented by an all 0
value. So I guess in any
reasonable implementation we would just have
#define ATOMIC_FLAG_INIT { 0 }
(or some equivalent formulation.)
If this is so, why ATOMIC_FLAG_INIT
at all? Why not
phrasing the same as for the other atomic types
Suggested Technical Corrigendum
Eliminate the mention of ATOMIC_FLAG_INIT
in 7.17.1p3,
B.16 and the index.
Proposed change for the initialization of atomic_flag
,
7.17.8p4:
The default initializer { 0 }
may be used to initialize
an atomic_flag
to the clear
state. An atomic_flag
object with automatic storage
duration that is not explicitly initialized using { 0 }
is initially in an indeterminate state; however, the default
(zero) initialization for objects with static or thread-local
storage duration initializes an atomic_flag
to the
clear state.
EXAMPLE
atomic_flag guard = { 0 };
If the committee would want to keep the
macro ATOMIC_FLAG_INIT
arround, a partial alternative
to the above text would be to modify the text in 7.17.1
ATOMIC_FLAG_INIT
which expands to a default initializer ({ 0 }
or
equivalent) for an object of type atomic_flag
.
Committee Discussion
Apr 2013 meeting
- The standard deliberately does not specify values for the clear and set states of
atomic_flag
objects in order to support the widest possible set of architectures.- The change suggested could be viewed as a proposal, but should be submitted as such, and since C11 shares the same memory model as C++11 a corresponding proposal should be submitted to WG21 as well.
Proposed Committee Response
The standard deliberately does not specify values for the clear and set states of atomic_flag objects in order to support the widest possible set of architectures. As such, the committee does not believe that this is a defect.
Submitter: Jens Gustedt
Submission Date:
2012-10-08
Source: WG14
Reference Document:
N1649
Subject: initialization of atomic types
Summary
The current version of the standard doesn't specify to which value an atomic object should be initialized if it is initialized by default.
An atomic object with automatic storage duration that is not
explicitly initialized using ATOMIC_VAR_INIT
is
initially in an indeterminate state; however, the default (zero)
initialization for objects with static or thread-local storage
duration is guaranteed to produce a valid state.
The mentioned valid state (in contrast to the indeterminate state
mentioned before) is thus a determinate state, but the value that is
stored is not mentioned explicitly. In the introduced language of
the standard it is no definition of a "determinate state". It could
be an "implementation-defined value", just an "unspecified value" or
a default (zero) initialization. Everything suggests the later,
that this would be the same value as for initializing a variable of
the underlying base type by { 0 }
. But I think it would
have helped to make that explicit.
Suggested Technical Corrigendum
Proposed change for the initialization of atomic objects, 7.17.2.1p2:
An atomic object with automatic storage duration that is not
explicitly initialized using ATOMIC_VAR_INIT
is
initially in an indeterminate state; however, the default (zero)
initialization for objects with static or thread-local storage
duration is guaranteed to produce a valid state that corresponds to
the value of a zero initialized object of the unqualified base
type.
EXAMPLE All three of the following objects initially have an
observable value of 0
.
_Atomic(unsigned) A = { 0 };
_Atomic(unsigned) B = ATOMIC_VAR_INIT(0u);
static _Atomic(unsigned) C;
Committee Discussion
Apr 2013 meeting
ATOMIC_VAR_INIT
is required to initialize an atomic object to a known value.- The default value for an atomic object is defined to be valid but is unspecified.
- The committee does not see this as a defect.
- N1649 proposes that the default value be specified to be the same as the non-atomic type's default value.
- Such a proposal should be submitted as such and may also need to be submitted to and addressed by WG21 (C++) as well.
Proposed Committee Response
ATOMIC_VAR_INIT
is required to initialize an atomic object to a known value. This value is defined to be valid but is unspecified in order to support the widest possible set of architectures. This is not a defect.
Submitter: Jens Gustedt
Submission Date:
2012-10-08
Source: WG14
Reference Document:
N1650
Subject: Defect Report relative to n1570: underspecification for
qualified rvalues
Summary
The dealing of rvalues with qualified types is largely underspecified in all versions of the C standard. This didn't surface as a problem until C11, since until then the type of an expression was not observable but only its value.
With C11 now a problem arises for type generic primary expressions;
with _Generic
type qualifications of values have become
observable.
The standard in any of its versions has not much to say when it comes to qualified types for rvalues. They definitively do exist, since the cast operator (6.5.4p2) explicitly specifies that the type could be qualified. That section on casts also has the only indication that relates to rvalues. There is a footnote (thus not normative) that says
89) A cast does not yield an lvalue. Thus, a cast to a qualified type has the same effect as a cast to the unqualified version of the type.
That could mean two things:
doing some tests I have found that clang and gcc disagree on this
point. (gcc doesn't have _Generic
, yet, but other builtins to observe
types)
clang seems to implement 1., gcc 2. They agree for lvalues like this
_Generic((double const){ 0 }, default: 0, double const: 1)
both evaluate it to 1
.
They disagree on the outcome for rvalues
_Generic((double const)0, default: 0, double const: 1)
clang gives 0
, gcc gives 1
.
(for gcc all with caution that it doesn't have _Generic
yet, but that this was obtained using an emulation of it by means of
other gcc builtins)
So that situation can easily lead to simple programs that have a behavior that depends on an undocumented choice and thus observe unspecified behavior.
This is not a defect of the _Generic
construct
itself. The intention is clearly to distinguish all types (with the
exception of VM types) that are not compatible, thus to allow to
distinguish all 8 different forms of qualifications of a type
(resp. 16 for pointer types) that can be obtained from the
qualifiers _Atomic
, const
,
volatile
(and restrict
).
For type generic expressions that are intended to operate on lvalues, such distinction can be crucial for any of the four qualifiers:
const
or volatile
qualified
lvalues there might be situations where application code might want
to use an unqualified compound literal in place of the controlling
expression._Atomic
qualified lvalues there might be
situations where application code might want to select a different
function than for expressions with same base type but without such a
qualifier.restrict
(or not) qualified pointers may enable an
application to select different algorithms or functions
(e.g memcopy
versus memmove
).Up to now, the conversions of 6.3.2.1 do not apply to primary expressions but only to operators. E.g in the following
double A[5]; double (*B)[5] = &A; double (*C)[5] = &(A);
B
and C
should be initialized to the same
value, the address of A
. If in (A)
the
primary expression ()
would enforce a decay of the
array to a pointer (and thus to an rvalue) the initialization
expression for C
would be a constraint violation.
So it seems obvious that the conversions in 6.3.2 ("Other Operands") are not intended to be applied to primary expressions.
Also the conversions in 6.3.2 are not consistent with respect to qualifiers. The only conversion that explicitly drops qualifiers is lvalue conversion (6.3.2.1p2). Array to pointer conversion (6.3.2.1p3) doesn't change qualifiers on the base type. Pointer conversion then, in 6.3.2.3, may add qualifiers to the base type when converting.
Two different constructs can be at the origin of a qualification of an rvalue:
Both constructs explicitly allow for qualifiers to be applied to the type. In particular 6.7.6.3p15 emphasizes (and constrains) the return type of function specifications to have compatible types, thus indicating that the qualification of the return type bares a semantic meaning.
If we suppose that any rvalue expression carries its qualification
further, other operations (e.g unary or binary +
) could
or could not result in qualified rvalues. The conversion rules in
6.3 and in particular the usual arithmetic conversions in 6.3.1.8
that allow to determine a common real type don't specify rules to
deal with qualifiers.
It seems that a lot of compilers already warn on such "superfluous" qualifications, but in view of type generic primary expression it is not clear that such warnings are still adequate.
C++ had to resolve this problem since its beginnings, because the feature of function overloading together with references of rvalues had made rvalue types and their qualifications observable.
Interestingly, to solve the problem the C++ refers to the C standard, claiming that C would drop all qualifiers for rvalue expressions that have scalar base type. It does this without refering to a particular text in the C standard, and in fact it can't since there doesn't seem to be such text.
The actual solution in C++ is thus that all rvalue expressions of
non-scalar types are const
-qualified and that those of
scalar types are unqualified. In view that scalar types are exactly
those types that are allowed to have cast operators that qualify the
type, all of this looks like a useless additional complication of
the issue.
Suggested Technical Corrigendum
There doesn't seem to be an easy solution to this defect, and the proposed solutions (as below or even differently) probably will need some discussion and investigations about their implications on existing code before a consensus could be reached.
This is (to my opinion) the worst solution, because the potential different code paths that an application code could take are numerous. There are 4 different qualifiers to handle and code that would have to rely on enumerating all combinations of different generic choices can quickly become a maintenance nightmare.
Also, implementations that chose to keep qualifiers on rvalues would have to decide (and document) by their own what the rules would be when operators are applied to such qualified rvalues.
For this solution in should be then elaborated how operators handle qualifiers. A natural way would be to accumulate qualifiers from operands with different qualifiers.
An important issue with this approach is the rapidly increasing number of cases, in particular 16 for pointer types. To keep the number of cases low when programming with type generic expressions we would need a generic tool for the following:
How to drop qualifiers for type generic expressions? Or alternatively add all qualifiers?
For arithmetic types with base type other
than _Bool
, char
, or short
something like the following would be useful:
+(X) // if unary plus drops all qualifiers (X) + (int const volatile _Atomic)0 // if qualifiers accumulateThis strategy wouldn't work for the narrow types, because the are promoted to
signed
or unsigned
.
This solution would already be a bit better than the previous one, since applications that compose type generic macros could select between two (or several) implementations. But the main problems (complexity and underspecification of operations) would remain.
This is not an ideal solution, since it would remove a lot of expressiveness from the generic selection construct. Lvalues could no be distinguished for their qualifiers:
void f(double*); #define F(X) _Generic((X), double: f)(&(X)) double const A = 42; F(A);
Here dropping the qualifiers of A
would result in a
choice of f
and in the evaluation
of f(&A)
. In case that f
modifies its
argument object (which we can't know) this would lead to undefined
behavior.
Not dropping the qualifiers would lead to a compile time constraint violation, because none of the types in the type generic expression matches. So here an implementation would be forced to issue a diagnostic, whereas if qualifiers are dropped the diagnostic is not mandatory.
This solution seems the one that is chosen by clang. It is probably
the easiest to specify. As mentioned above it has the disadvantage
that the two very similar expressions (int const){0}
and (int const)0
have different types.
Some clarification should be added to the standard, though.
6.5.1.1, modify as follows:
EXAMPLE The cbrt
type-generic macro could be implemented as
follows. Here the prefix operator +
in the selection
expression ensures that lvalue conversion on arithmetic
types is performed such that
e.g lvalues of type float const
select cbrtf
and not the default cbrt
.
#define cbrt(X) _Generic(+(X), \ long double: cbrtl, \ default: cbrt, \ float: cbrtf \ )(X)
6.5.2.2, add after p1: The type of a function call is the return type of the function without any qualifiers.
6.5.4, add after p2: The type of a cast expression of a qualified scalar type is the scalar type without any qualifiers.
6.7.63, change p15, first sentence: For two function types to be compatible, the unqualified versions of both return types shall be compatible.
C11: When introduced like this, this will invalidate some valid C11 programs, since some type generic expression might behave differently. The faster such corrigendum is adopted the less likely it is that such programs exist.
const
qualifier to all types for rvalues
Analogous as in the case above it has the disadvantage that the two
very similar expressions (int){0}
and (int)0
have different types.
This is my favorite solution, since it also "repairs" another issue that I am unconfortable with: the problem of array decay in objects with temporary lifetime:
struct T { double a[4]; } A; struct T f(void) { return (struct T){ 0 }; } double g0(double* x) { return *x; } ... g0(f().a);
Here f()
is an rvalue that results in an object of
temporary lifetime struct T
and then f().a
decays to a double*
. Semantically a better solution
would be that it decays to a double const*
since a
modification of the value is not allowed (undefined
behavior). Already with C99 it would be clearer to
declare g1
as:
double g1(double const* x) { return *x; }
If f()
would be of type struct T const
,
f().a
would decay to a double const*
. Then
a call of g0
would be a constraint violation
and g1
would have to be used.
The necessary changes to the standard would be something like:
6.5.1.1, modify as follows:
EXAMPLE The cbrt
type-generic macro could be implemented as
follows. Here the prefix operator +
in the selection
expression ensures that lvalue conversion on arithmetic
types is performed such that
e.g lvalues of type float
select cbrtf
and not the default cbrt
.
#define cbrt(X) _Generic(+(X), \ long double const: cbrtl, \ default: cbrt, \ float const: cbrtf \ )(X)
6.5.2.2, add after p1: The type of a function call is
the const
-qualified return
type of the function without any other qualifiers.
6.5.4, add after p2: The type of a cast expression of a qualified
scalar type is the const
-qualified scalar type without
any other qualifiers.
6.7.63, change p15, first sentence: For two function types to be compatible, the unqualified versions of both return types shall be compatible.
C11: When introduced like this, this will invalidate some valid C11 programs, since some type generic expression might behave differently. The faster such corrigendum is adopted the less likely it is that such programs exist.
C99: When introduced like this, this will invalidate some
valid C99 programs that pass rvalue pointers as presented above to
function parameters that are not const
-qualified but
where the called function then never modifies the object of
temporary lifetime behind the pointer. Unless for very old legacy
functions (from before the introduction of const
to the
language) such interfaces should be able to use the
"correct" const
-qualification, or they could be
overloaded with a type generic interface that takes care of that
issue.
Committee Discussion
Apr 2013 meeting
- This paper is new enough that a thorough examination of its contents has not been made. It is not clear whether it is a DR or a proposal.
- If implementers do not know what to do, it is a defect.
- Handling of the atomic type qualifier may be the most likely defect, if there is one.
Committee Discussion
Preceding an expression by a parenthesized type name converts the value of the expression to the unqualified version of the named type. This construction is called a cast. A cast that specifies no conversion has no effect on the type or value of an expression.Also, footnote 104 should be reduced to just its first sentence.
and the type specified for ident in the declaration "
T D
" is "derived-declarator-type-list T", then the type specified for ident is "derived-declarator-type-list function returning the unqualified version of T".
Committee Discussion
Preceding an expression by a parenthesized type name converts the value of the expression to the unqualified version of the named type. This construction is called a cast. A cast that specifies no conversion has no effect on the type or value of an expression. (and delete footnote 104)
. . . then the type specified for ident is "derived-declaration-type-list function returning the unqualified version of T".
Apr 2014 meeting
Committee Discussion
Submitter: Jens Gustedt
Submission Date: 2012-10-08
Source: WG14
Reference Document: N1651
Subject: underspecification of tss_t
Summary
Section 7.26.6 “Thread-specific storage functions” of C11 is
severely underspecified since it uses terms that are not introduced (so
far) in the context of C. This is really a pity, since POSIX also has pthread_key_t
that is completely feature equivalent and for which the specification
is much more complete.
Jacob Navia had observed that at several occasions in comp.std.c
but it seems that he had not got enough attention such that this had
made it in a defect report.
The tss_create
function creates a
thread-specific storage pointer with destructor dtor
,
which may be null.
The main problem is that it is nowhere explained/defined
TSS_DTOR_ITERATIONS
would be.Suggested Technical Corrigendum
I think several paragraphs should be added after the one above:
The effect is that for each thread that has the thread specific storage corresponding tokey
set to a valuex
that is not null, the destructor function*dtor
is called withdtor(x)
before the thread exits.
This call todtor
is executed in the context of the same thread; it is sequenced after thereturn
statement or the call tothrd_exit
that terminates the thread and before any return fromthrd_join
of a waiter for this same thread. If there are several thread specific storages for the same thread their destructor functions are called in an unspecific order but with a sequence point between each of these function calls.
If a destructor function forkey
issues calls totss_set
,tss_get
ortss_delete
with the samekey
the behavior is undefined.
tss_set
can be used to set the value of a thread specific storage for a different keykey2
that had not been set before or that has been processed with a call to the corresponding destructor.
By that the set of thread specific storages for a given thread may change during the execution of the corresponding destructors.
If after processing all tss that are active at thereturn
of the thread function or at the end ofthrd_exit
there are still tss that are active the procedure of calling destructors is iterated. An implementation may bind the maximum number such of supplementary iterations byTSS_DTOR_ITERATIONS
.
A second problem is that there are two functionalities that are
easily mixed up and which interrelationship should be clarified: the
destructor that is called (let us suppose this) at exit of a thread,
and tss_delete
that deletes a thread specific storage for
all running threads. I think something like the following should be
added in 7.26.6.2 after para 2:
The deletion ofkey
will not change the observable behavior of any of the active threads. Iftss_delete
is called forkey
and there is a thread that has a non-null value forkey
that has passed a terminatingreturn
statement or call tothrd_exit
but not yet finished the processing of all its tss destructors, the behavior is undefined.
Committee Discussion
These issues are covered under DR 416. See discussion there.
Apr 2013 meeting
Committee Discussion
In addition to DR 416 this report suggests defining as undefined behavior the interaction ofOct 2013 meetingthrd_exit
andtss_delete
.
Proposed Committee Response
The issues raised herein have been considered in conjunction with DR 416 and are jointly resolved in that DR's Proposed Technical Corrigendum.
Submitter: Jens Gustedt
Submission Date:
2012-10-08
Source: WG14
Reference Document:
N1653
Subject: no specification for the access to variables with
temporary lifetime
Summary
Section 6.2.4 in p4 and p5 requires implementation defined behavior for accessing objects with thread local or automatic storage from different threads than where they are defined. No such mention is done for objects with temporary lifetime in p8. Can they be accessed by other threads? Is this property handled similar to the property for automatic storage duration? Or should this simply be forbidden?
Suggested Technical Corrigendum
Add to the end of 6.2.4 p8:
The result of attempting to indirectly access an object with temporary lifetime from a thread other than the one with which the object is associated is implementation-defined.
Add to 7.26.1p3:
__STDC_THREAD_TEMPORARY_VISIBLE__
which expands to 1 if objects of temporary lifetime are visible to other threads and to 0 otherwise.
Proposed Committee Response
Objects with temporary lifetime are defined in 6.2.4 paragraph 8 to be those with automatic storage duration, and so inter-thread access is implementation defined. No change needed.
Submitter: Fred J. Tydeman
Submission Date: 2013-01-07
Source: WG 14
Reference Document: N1670
Subject: G.5.1: -yv and -x/v are ambiguous
Summary
The tables in G.5.1 have the mathematical formulas -yv and -x/v. I believe that they are ambiguous as they could have two meanings:
I believe it matters for at least these cases:
Suggested Technical Corrigendum
Committee Discussion
Committee Discussion
Proposed Technical Corrigendum
In the table in G.5.1 #2, change
-yvto
(-y)vin three places.In the table in G.5.1 #3, change
-x/vto
(-x)/vin two places.
Submitter: Shao Miller <sha0.miller@gmail.com>
Submission Date: 2013-01-24
Source: WG 14
Reference Document:
N1671
Subject: Function Parameter and Return Value Assignments
Summary
The wording for the the assignments of function arguments to function
parameters and for the assignment of a return
statement's expression to the value of the function call can
potentially be confused.
6.5.2.2p2:
If the expression that denotes the called function has a type that includes a prototype, the number of arguments shall agree with the number of parameters. Each argument shall have a type such that its value may be assigned to an object with the unqualified version of the type of its corresponding parameter.
The appearance of "may be assigned" can lead to the question (#1) of whether or not the constraints and semantics under both 6.5.16 and 6.5.16.1 might apply. The Forward references indicate 6.5.16.1, so this question might be unwarranted.
The appearance of "unqualified version of the type of its corresponding parameter" does not match 6.9.1p10, which doesn't use "unqualified" (see below).
6.5.16p2:
An assignment operator shall have a modifiable lvalue as its left operand.
If 6.5.2.2p2's mention of "assigned" implies this constraint as a secondary constraint, it is not clear which "modifiable lvalue" or even "lvalue" would ever satisfy the constraint. The "modifiable lvalue" does not appear to be the parameter, because:
6.7.3p4:
The properties associated with qualified types are meaningful only for expressions that are lvalues.132)
132) The implementation may place a const object that is not volatile in a read-only region of storage. Moreover, the implementation need not allocate storage for such an object if its address is never used.
This can suggest that 6.5.2.2p2's "an object with the unqualified version of the type" implies an lvalue, but (question #2) is it a modifiable lvalue? Question #3: If the type is a structure or union type with a const-qualified member (possibly via recursion), are the members considered to be unqualified, too? If so, this is an important difference from pointer types where the referenced type (or its referenced type, recursively) would not be considered unqualified. Also worth consideration would be an array object (which is not qualified) having elements matching such a structure or union type (possibly via recursion).
The return type of a function might be const-qualified, or might be a structure or union type having such a member (possibly via recursion). Question #4: Should the return type of a function be adjusted to be an unqualified version of the type? Such an adjustment might have implications for type compatibility and composite type and might be better off left alone. (const is being used for illustrative purposes, but all type qualifiers can equally be considerations.)
6.8.6.4p3:
If a return statement with an expression is executed, the value of the expression is returned to the caller as the value of the function call expression. If the expression has a type different from the return type of the function in which it appears, the value is converted as if by assignment to an object having the return type of the function.160)
160) The return statement is not an assignment. The overlap restriction of subclause 6.5.16.1 does not apply to the case of function return. The representation of floating-point values may have wider range or precision than implied by the type; a cast may be used to remove this extra range and precision.
If the return type of a function is const-qualified, or is a structure or union type having such a member (possibly via recursion), then "as if by assignment" works for 6.5.16.1, but the constraint of 6.5.16p2 requires a "modifiable lvalue".
The footnote reminds us that a return statement with an expression is not an assignment, but it is not clear that only 6.5.16.1 applies for the "as if by assignment" case.
6.9.1p10:
On entry to the function, the size expressions of each variably modified parameter are evaluated and the value of each argument expression is converted to the type of the corresponding parameter as if by assignment. (Array expressions and function designators as arguments were converted to pointers before the call.)
6.9.1p11:
After all parameters have been assigned, the compound statement that constitutes the body of the function definition is executed.
A const-qualified lvalue cannot normally be assigned-to. An lvalue for an object having a structure or union type containing a const-qualified member (possible via recursion) cannot normally be assigned-to.
6.9.1p10 doesn't match the use of "unqualified" in 6.5.2.2p2 (see above).
Suggested Technical Corrigendum
Sun c99 and GCC disagree on the return statement's semantics.
Change 6.5.2.2p2 to:
If the expression that denotes the called function has a type that includes a prototype, the number of arguments shall agree with the number of parameters. Each argument shall be such that it satifies the constraints of simple assignment when considering the argument to be the right operand and considering the left operand to have the unqualified version of the type of the corresponding parameter.
(Loosely establishes an example for "as if by simple assignment".)
Change 6.8.6.4p3 to:
If a return statement with an expression is executed, the value of the expression is returned to the caller as the value of the function call expression. If the expression has a type different from the return type of the function in which it appears, the value is converted as if by simple assignment to an object having the unqualified version of the return type of the function.160)
Change 6.9.1p10 to:
On entry to the function, the size expressions of each variably modified parameter are evaluated in an unspecified order, the value of each argument expression is converted to the unqualified version of the type of the corresponding parameter as if by simple assignment, then each converted value becomes the initial value for the corresponding parameter. (Array expressions and function designators as arguments were converted to pointers before the call.)
Change 6.9.1p11 to:
After all parameters have initial values, the compound statement that constitutes the body of the function definition is executed.
Add bullet to J.1
- The order in which the size expressions of variably modified parameters are evaluated upon function entry (6.9.1).
Apr 2013 meeting
Committee Discussion
If the expression that denotes the called function has a type that includes a prototype, the number of arguments shall agree with the number of parameters. Each argument shall have a type such that its value may be assigned to an object with the unqualified version of the type of its corresponding parameter.
to
If the expression that denotes the called function has a type that includes a prototype, the number of arguments shall agree with the number of parameters. Each argument shall have a type such that its value may be used to initialize an object having the type of its corresponding parameter.
In 6.5.2.2p4 change
An argument may be an expression of any complete object type. In
preparing for the call to a function, the arguments are evaluated, and
each parameter is assigned to the value of the corresponding argument.
to
An argument may be an expression of any complete object type. In preparing for the call to a function, the arguments are evaluated, and each parameter is initialized to the value of the corresponding argument.
Committee Discussion
The issue of conversion has to do with whether there are differing promotions and type conversions that would apply when constructing an argument list that would not occur if these expressions were used as initializers in a declaration.
Oct 2014 meeting
Committee Discussion
The committee concluded after a discussion that there were no promotion or type conversion issues raised by the proposed wording above, and that the following should be adopted as a Proposed Technical Corrigendum.
Proposed Technical Corrigendum
In 6.5.2.2p2 change:
If the expression that denotes the called function has a type that includes a prototype, the number of arguments shall agree with the number of parameters. Each argument shall have a type such that its value may be assigned to an object with the unqualified version of the type of its corresponding parameter.
to
If the expression that denotes the called function has a type that includes a prototype, the number of arguments shall agree with the number of parameters. Each argument shall have a type such that its value may be used to initialize an object having the type of its corresponding parameter.
In 6.5.2.2p4 change
An argument may be an expression of any complete object type. In preparing for the call to a function, the arguments are evaluated, and each parameter is assigned the value of the corresponding argument.
to
An argument may be an expression of any complete object type. In preparing for the call to a function, the arguments are evaluated, and each parameter is initialized to the value of the corresponding argument.
Submitter: Douglas Walls
Submission Date: 2013-02-11
Source: WG 14
Reference Document:
N1672
Subject: runtime-constraint issue with sprintf family of
routines in Annex K
Summary
snprintf_s (Annex K.3.5.3.5)Suggested Technical Corrigendum
snprintf_s
Replace K.3.5.3.5p3 with:
If there is a runtime-constraint violation, then if s is not
a null
pointer and n is greater than zero and not greater than
RSIZE_MAX, then the
snprintf_s function sets s[0] to the null character.
sprintf_s
Replace K.3.5.3.6p3 with:
If there is a runtime-constraint violation, then if s is not
a null
pointer and n is greater than zero and not greater than
RSIZE_MAX, then the
sprintf_s function sets s[0] to the null character.
vsnprintf_s
Replace K.3.5.3.12p3 with:
If there is a runtime-constraint violation, then if s is not
a null
pointer and n is greater than zero and not greater than
RSIZE_MAX, then the
vsnprintf_s function sets s[0] to the null character.
vsprintf_s
Replace K.3.5.3.13p3 with:
If there is a runtime-constraint violation, then if s is not
a null
pointer and n is greater than zero and not greater than
RSIZE_MAX, then the
vsprintf_s function sets s[0] to the null character.
Committee Discussion
Committee Discussion
Proposed Technical Corrigendum
snprintf_s
Replace K.3.5.3.5p3 with:
If there is a runtime-constraint violation, then if s is not
a null
pointer and n is greater than zero and not greater than
RSIZE_MAX, then the
snprintf_s function sets s[0] to the null character.
sprintf_s
Replace K.3.5.3.6p3 with:
If there is a runtime-constraint violation, then if s is not
a null
pointer and n is greater than zero and not greater than
RSIZE_MAX, then the
sprintf_s function sets s[0] to the null character.
vsnprintf_s
Replace K.3.5.3.12p3 with:
If there is a runtime-constraint violation, then if s is not
a null
pointer and n is greater than zero and not greater than
RSIZE_MAX, then the
vsnprintf_s function sets s[0] to the null character.
vsprintf_s
Replace K.3.5.3.13p3 with:
If there is a runtime-constraint violation, then if s is not
a null
pointer and n is greater than zero and not greater than
RSIZE_MAX, then the
vsprintf_s function sets s[0] to the null character.
Submitter: Douglas Walls
Submission Date: 2013-02-11
Source: WG 14
Reference Documents:
N1673 N1748
Subject: Should gets_s discard next input line when (s == NULL) ?
Summary
gets_s Annex K.3.5.4.1p2 says:Suggested Technical Corrigendum
Committee Discussion
Committee Discussion
Proposed Technical Corrigendum
If there is a runtime-constraint violation, characters are read and discarded from stdin until a new-line character is read, or end-of-file or a read error occurs, and if s is not a null pointer s[0] is set to the null character.
Submitter: Douglas Walls
Submission Date: 2013-02-11
Source: WG 14
Reference Document: N1674
Subject: getenv_s, maxsize should be allowed to be
zero
Summary
getenv_s, Annex K.3.6.2.1p2 under Runtime-constraints says:Suggested Technical Corrigendum
Replace Annex K.3.6.2.1p2 second sentence with:
maxsize shall not be
greater than RSIZE_MAX.
K.3.6.2.1p2 would then read thusly:
name shall not be a null
pointer. maxsize
shall not be greater than
RSIZE_MAX. If
maxsize is not equal to
zero, then value shall not
be a null pointer.
Committee Discussion
Proposed Technical Corrigendum
Replace Annex K.3.6.2.1p2 second sentence with:
maxsize shall not be
greater than RSIZE_MAX.
K.3.6.2.1p2 would then read thusly:
name shall not be a null
pointer. maxsize
shall not be greater than
RSIZE_MAX. If
maxsize is not equal to
zero, then value shall not
be a null pointer.
Submitter: Douglas Walls
Submission Date: 2013-02-21
Source: WG14
Reference Document: N1675
Subject: atomic_compare_exchange: What does it mean to say two
structs compare equal?
Summary
7.17.7.4 The atomic_compare_exchange generic functionsSuggested Technical Corrigendum
Either define equality of objects of struct type, add a restriction
disallowing
use of atomic structs as arguments for the atomic_compare_exchange
generic functions,
or note that atomic_compare_exchange generic functions for objects of
atomic
struct type results in undefined behavior.
Committee Discussion
Committee Discussion
Apr 2014 meeting
Committee Discussion
Oct 2014 meeting
Committee Discussion
As requested, the paper N1864 was written and provided. From our C++ liaison, however, it was learned that corresponding behavior is well defined and is in use. Further investigation revealed that atomic_compare_exchange in C++ is and has been explicitly defined to be that of bit comparison. C11 defines it as value comparison.It was noted that bit comparison for atomic bool would not give the expected answer if differing non-zero "true" values were compared. It was also noted that bit comparison exposes padding bits, whereas lock bits would be required to be discarded, leading to code that might work on one implementation of an architecture but fail on another.
A new paper was solicited.
Submitter: Fred J. Tydeman (USA)
Submission Date: 2013-03-07
Source: WG 14
Reference Document: N1677
Subject: Possible defect report: Is 0.0 required to be a
representable value?
There are many places in C11 that assume a floating-point zero value, e.g., 0.0, is representable.
When a value of real type is converted to a complex type, the real part of the complex result value is determined by the rules of conversion to the corresponding real type and the imaginary part of the complex result value is a positive zero or an unsigned zero.
if it has arithmetic type, it is initialized to (positive or unsigned) zero;
If no conversion could be performed, zero is returned.
If no conversion could be performed, zero is returned.
There are many places in C11 that allow for a representable floating-point zero value.
An implementation may give zero and values that are not floating-point numbers (such as infinities and NaNs) a sign or may leave them unsigned.
The C Rationale in its discussion of 5.2.4.2.2 has:
Note that the floating-point model adopted permits all common representations, including sign-magnitude and two's-complement, but precludes a logarithmic implementation.The C89 Committee also endeavored to accommodate the IEEE 754 floating-point standard by not adopting any constraints on floating-point which were contrary to that standard.
However, if one carefully reads 5.2.4.2.2 Characteristics of floating types <float.h>, #2 and #3, one finds that zero is not required to be representable. As I read those paragraphs, normalized floating-point numbers are the only things required to be contained in floating types. Subnormal floating-point numbers, unnormalized floating-point numbers, infinities, and NaNs are additional (optional) things that may be contained in floating types. Zero is not mentioned explicitly.
So, it appears that some parts of C11 require that floating-point zeros be representable, while other parts do not require that they be representable.
I think that the first sentance in 5.2.4.2.2 #3 should be changed to:
Floating types shall be able to represent normalized floating-point numbers (f1 > 0 if x != 0) and (positive or unsigned) zero. In addition, floating types may be able to contain other kinds of floating-point numbers, such as negative zero and subnormal ...
Suggested Technical Corrigendum
Committee Discussion
Proposed Technical Corrigendum
The first sentance in 5.2.4.2.2 #3 should be changed to:
Floating types shall be able to represent normalized floating-point numbers (f1 > 0) and (positive or unsigned) zero. In addition, floating types may be able to contain other kinds of floating-point numbers, such as negative zero and subnormal ...
Submitter: Douglas Walls
Submission Date: 2013-03-12
Source: WG 14
Reference Document: N1683
N1771
Subject: Issue with constraints for wide character function
arguments involving RSIZE_MAX
Summary
K.3.7.2.2 The strncat_s functionSuggested Technical Corrigendum
Committee Discussion
Committee Discussion
Apr 2014 meeting
Committee Discussion
A "nor nor" typo in the Suggested Technical Corrigendum for K.3.9.3.2.2p12 was noticed and corrected in the Proposed Technical Corrigendum below.
Proposed Technical Corrigendum
K.3.6.5.1 The mbstowcs_s function
In K.3.6.5.1p2, replace "RSIZE_MAX" with "RSIZE_MAX/sizeof(wchar_t)".
In K.3.6.5.1p3, replace "less than RSIZE_MAX" with
"not greater than RSIZE_MAX/sizeof(wchar_t)".
K.3.6.5.2 The wcstombs_s function
In K.3.6.5.2p2, replace "then neither len nor dstmax shall be greater
than RSIZE_MAX" with
"then neither len shall be greater than RSIZE_MAX/sizeof(wchar_t) nor
dstmax shall be greater than RSIZE_MAX".
In K.3.6.5.2p3, replace "less than RSIZE_MAX" with "not greater than
RSIZE_MAX".
K.3.9.1.3 The snwprintf_s function
In K.3.9.1.3p2, replace "RSIZE_MAX" with
"RSIZE_MAX/sizeof(wchar_t)".
In K.3.9.1.3p3, replace "less than RSIZE_MAX" with "not greater
than RSIZE_MAX/sizeof(wchar_t)". See DR 428
K.3.9.1.4 The swprintf_s function
In K.3.9.1.4p2, replace "RSIZE_MAX" with
"RSIZE_MAX/sizeof(wchar_t)".
In K.3.9.1.4p3, replace "less than RSIZE_MAX" with "not greater
than RSIZE_MAX/sizeof(wchar_t)". See DR 428
K.3.9.1.8 The vsnwprintf_s function
In K.3.9.1.8p2, replace "RSIZE_MAX" with
"RSIZE_MAX/sizeof(wchar_t)".
In K.3.9.1.8p3, replace "less than RSIZE_MAX" with "not greater
than RSIZE_MAX/sizeof(wchar_t)". See DR 428
K.3.9.1.9 The vswprintf_s function
In K.3.9.1.9p2, replace "RSIZE_MAX" with
"RSIZE_MAX/sizeof(wchar_t)".
In K.3.9.1.9p3, replace "less than RSIZE_MAX" with "not greater
than RSIZE_MAX/sizeof(wchar_t)". See DR 428
K.3.9.2.1.1 The wcscpy_s function
In K.3.9.2.1.1p2, replace "RSIZE_MAX" with
"RSIZE_MAX/sizeof(wchar_t)".
In K.3.9.2.1.1p3, replace "RSIZE_MAX" with
"RSIZE_MAX/sizeof(wchar_t)".
K.3.9.2.1.2 The wcsncpy_s function
In K.3.9.2.1.2p8, replace "RSIZE_MAX" with
"RSIZE_MAX/sizeof(wchar_t)".
In K.3.9.2.1.2p9, replace "RSIZE_MAX" with
"RSIZE_MAX/sizeof(wchar_t)".
K.3.9.2.1.3 The wmemcpy_s function
In K.3.9.2.1.3p15, replace "RSIZE_MAX" with
"RSIZE_MAX/sizeof(wchar_t)".
In K.3.9.2.1.3p16, replace "RSIZE_MAX" with
"RSIZE_MAX/sizeof(wchar_t)".
K.3.9.2.1.4 The wmemmove_s function
In K.3.9.2.1.4p20, replace "RSIZE_MAX" with
"RSIZE_MAX/sizeof(wchar_t)".
In K.3.9.2.1.4p21, replace "RSIZE_MAX" with
"RSIZE_MAX/sizeof(wchar_t)".
K.3.9.2.2.1 The wcscat_s function
In K.3.9.2.2.1p3, replace "RSIZE_MAX" with
"RSIZE_MAX/sizeof(wchar_t)".
In K.3.9.2.2.1p4, replace "RSIZE_MAX" with
"RSIZE_MAX/sizeof(wchar_t)".
K.3.9.2.2.2 The wcsncat_s function
In K.3.9.2.2.2p10, replace "RSIZE_MAX" with
"RSIZE_MAX/sizeof(wchar_t)".
In K.3.9.2.2.2p11, replace "RSIZE_MAX" with
"RSIZE_MAX/sizeof(wchar_t)".
K.3.9.2.3.1 The wcstok_s function
In K.3.9.2.3.1p2, replace "RSIZE_MAX" with
"RSIZE_MAX/sizeof(wchar_t)".
K.3.9.3.2.1 The mbsrtowcs_s function
In K.3.9.3.2.1p3, replace "RSIZE_MAX" with
"RSIZE_MAX/sizeof(wchar_t)".
In K.3.9.3.2.1p4, replace "less than RSIZE_MAX" with "not greater
than RSIZE_MAX/sizeof(wchar_t)". See DR 428
K.3.9.3.2.2 The wcsrtombs_s function
In K.3.9.3.2.2p12, replace "then neither len nor dstmax shall be
greater than RSIZE_MAX" with
"then neither len shall be greater than RSIZE_MAX/sizeof(whcar_t)
nor dstmax shall be greater than RSIZE_MAX".
In K.3.9.3.2.2p13, replace "less than RSIZE_MAX" with "not greater than
RSIZE_MAX".
Submitter: Fred J. Tydeman (USA)
Submission Date: 2012-10-26
Source: WG 14
Reference Document:
N1660
N1660
Subject:Possible defect report: Missing constraint w.r.t. Atomic
Summary
6.7.2.4 Atomic type specifiers, has in paragraph 2:
Atomic type specifiers shall not be used if the implementation does not support atomic types (see 6.10.8.3).
But, 6.7.3 Type qualifiers, has no similar constraint with respect to _Atomic.
Also, 7.17.6 Atomic integer types, has no similar constraint. Aside: The only constraints I see in the library are in <float.h> and <limits.h>, so it is not clear if this case should be a constraint.
Suggested Technical Corrigendum
Add to 6.7.3 Type qualifiers, a new paragraph after paragraph 3,
Atomic type qualifiers shall not be used if the implementation does not support atomic types (see 6.10.8.3).
Add to 7.16.6 Atomic integer types, a new paragraph before paragraph 1:
ConstraintsAtomic type names shall not be used if the implementation does not support atomic types (see 6.10.8.3).
Committee Discussion
Proposed Technical Corrigendum
Add to 6.7.3 Type qualifiers, a new paragraph after paragraph 3,
Atomic type qualifiers shall not be used if the implementation does not support atomic types (see 6.10.8.3).
Submitter: Fred J. Tydeman (USA)
Submission Date: 2012-10-26
Source: WG 14
Reference Document:
N1661
Subject:Possible defect report: Missing constraint w.r.t.
Imaginary
Summary
The type specifier _Complex shall not be used if the implementation does not support complex types (see 6.10.8.3).
But, G.2 Types, has no similar constraint with respect to _Imaginary.
Suggested Technical Corrigendum
Add to G.2 Types, a new sentence in paragraph 1:
The _Imaginary type specifier shall not be used if the implementation does not support imaginary types (see 6.10.8.3).
Committee Discussion
This is not actually a defect.
Proposed Committee Response
Annex G requires _Imaginary be supported, so there is no need to cite a requirement for when it is not supported.
Submitter: Willem Wakker (NL)
Submission Date: 2013-05-08
Source: WG 14
Reference Document:
N1713
Subject: Request for interpretation of C11 6.8.5#6
C11, section 6.8.5 paragraph 6 reads:
An iteration statement whose controlling expression is not a constant expression,156) that performs no input/output operations, does not access volatile objects, and performs no synchronization or atomic operations in its body, controlling expression, or (in the case of a for statement) its expression-3, may be assumed by the implementation to terminate.157)
Question: to what does the that refers back to: to the
controlling expression or to the constant expression?
Committee Discussion
This is indeed an ambiguity, and after considering various proposals, the following was approved.
Apr 2014 meeting
Committee Discussion
The committee noted a typo in the Suggested Technical Corrigendum where "its expression *157" was intended to be "its expression-3 *157", and so has been corrected below.
Proposed Technical Corrigendum
Replace 6.8.5 paragraph 6 with:An iteration statement may be assumed by the implementation to terminate if its controlling expression is not a constant expression *156), and none of the following operations are performed in its body, controlling expression or (in the case of a for statement) its expression-3 *157):
- input/output operations
- accessing a volatile object
- synchronization or atomic operations.
Submitter: Nick Stoughton (US)
Submission Date: 2013-06-19
Source: Austin Group
Reference Document:
N1719
Subject: clock
overflow
problems
Summary
C11 (and C99 before it) state for clock() that
If the processor time used is not available or its value cannot be represented, the function returns the value (clock_t)(-1).(C11 7.27.2.1 p3). Footnote 319 also states
In order to measure the time spent in a program, the clock function should be called at the start of the program and its return value subtracted from the value returned by subsequent calls.
The normative requirement implies that if more processor time has passed than can be fit into a variable of type clock_t the function must fail and return (clock_t)-1.
However, existing implementations almost exclusively ignore this requirement and if more ticks pass than can fit into a clock_t the implementation simply truncates the value and return the lowermost bits of the actual value. In programming environments where clock_t is a 32-bit integer type and CLOCKS_PER_SEC is one million (a very common implementation), clock() will start misreporting in less than 36 minutes of processor time for signed clock_t, or 72 minutes for unsigned clock_t.
Question 1: Are such implementations conforming? If not, should the standard be altered in any way to permit this de-facto standard implementation?
Question 2: Should the standard define some limit macros for clock_t (effectively defining new values in limits.h for CLOCK_MAX, the minimum maximum value for a clock_t)?
Question 3: If the value is truncated and clock_t is a signed type, the recommended application usage n footnote 319 (subtracting clock_t values to measure intervals) can cause the application to invoke undefined behavior via integer overflow. In particular, if the initial call to clock() returned A > 0 (by virtue of some processor time having been consumed before the start of main() or the point of first measurement), and a subsequent call returned B=INT_MIN just after overflow, then the recommended practice of computing B-A invokes undefined behavior. Should there be any warning of this included in the footnote?
Suggested Change
Given that the vast majority of surveyed implementations appear to have implemented clock with a simple incrementing counter with no check for overflow, the requirement for clock() to return (clock_t)-1 when the number of clock ticks cannot be represented in a variable of type clock_t should be relaxed:
At 7.27.2.1 paragraph 3, change:
If the processor time used is not available or its value cannot be represented, the function returns the value (clock_t)(-1).to:
If the processor time used is not available, the function returns the value (clock_t)-1.(thus leaving the behavior on overflow unspecified). Change footnote 319 to:
In order to measure the time spent in a program, the clock function should be called at the start of the program and its return value subtracted from the value returned by subsequent calls. Note, however, that such a subtraction may result in undefined behavior if clock_t is an unsigned integer type.
Committee Discussion
Implementations commonly use an integer clock_t type that can overflow in as little as 36 minutes. All uses of clock() to measure a duration of time must address the issue of possible overflow.
Apr 2014 meeting
Committee Discussion
The author will be solicited for a revised technical corrigendum.
Oct 2014 meeting
Committee Discussion
There was no paper submitted on this topic, and the committee will again solicit the Austin Group for direction.
Submitter: Nick Stoughton (US)
Submission Date: 2013-06-19
Source: Austin Group
Reference Documents: N1720,
Austin Group Defect #701
Subject: ungetc / ungetwc
and file position after discarding push back
problems
Summary
C11 (and C99 before it) state for both ungetc() and ungetwc() that
A successful intervening call (with the stream pointed to by stream) to a file positioning function (fseek, fsetpos, or rewind) discards any pushed-back characters for the stream. ... The value of the file position indicator for the stream after reading or discarding all pushed-back characters shall be the same as it was before the characters were pushed back.(7.21.7.10 p2 & p5, with similar at 7.29.3.10 p2 & p5). The "or discarding" phrasing in p5 makes no sense: all of the listed functions which discard the push back also _set_ the file position. The file position will end up as whatever the function sets it to, not "the same as it was before the characters were pushed back".
Suggested Change
ChangeThe value of the file position indicator for the stream after reading or discarding all pushed-back characters shall be the same as it was before the characters were pushed back.to
The value of the file position indicator for the stream after all pushed-back characters have been read shall be the same as it was before the characters were pushed back.
Oct 2013 meeting
Committee Discussion
Apr 2014 meeting
Committee Discussion
The Standard is correct as written because the intent is that the specified file position indicator is an intermediate state inside the file positioning function after the pushed-back characters are discarded but before the actual seek. That gives you a reliable file position from which to do the seek. It's not intended that the file positioning function doesn't set the file position indicator.
Proposed Technical Corrigendum
Add a footnote to 7.21.7.10 paragraph 5, second sentence:
Note that a file positioning function may further modify the file position indicator after discarding any pushed-back characters.
Note that a file positioning function may further modify the file position indicator after discarding any pushed-back wide characters.
Submitter: Clark Nelson
Submission Date: 2013-07-16
Source: WG 14
Reference Document:
N1729
Subject: Issues with the definition of “full expression”
Summary
I have discovered several issues in 6.8p4, which defines “full expression” and points out the major implications for an expression that is a full expression. In this paper I present the issues, along with my recommendations. For the issues for which it makes sense, I will later submit defect reports.
Here is the text of 6.8p4, with clause numbering added for convenience of reference:
- A full expression is an expression that is not part of another expression or of a declarator.
- Each of the following is a full expression:
- an initializer that is not part of a compound literal;
- the expression in an expression statement;
- the controlling expression of a selection statement (if or switch);
- the controlling expression of a while or do statement;
- each of the (optional) expressions of a for statement;
- the (optional) expression in a return statement.
- There is a sequence point between the evaluation of a full expression and the evaluation of the next full expression to be evaluated.
And here are the issues.
The phrase “not part of another expression or of a declarator” (sentence 1) is rather difficult to understand. It probably means: not part of another expression, nor part of a declarator. (But DeMorgan's law is hard on the brain.)
I believe this could be fixed as a simple editorial issue.
The status of an initializer expression depends on whether the context is a declaration or a compound literal (clause 2.1). That would seem to imply different sequencing guarantees in those contexts. As it turns out, it does, but the implication is quite subtle. Consider 6.7.9p23:
The evaluations of the initialization list expressions are indeterminately sequenced with respect to one another and thus the order in which any side effects occur is unspecified.
And consider this example:
#include <stdio.h> #define ONE_INIT '0' + i++ % 3 #define INITIALIZERS [2] = ONE_INIT, [1] = ONE_INIT, [0] = ONE_INIT int main() { int i = 0; char x[4] = { INITIALIZERS }; // case 1 puts(x); puts((char [4]){ INITIALIZERS }); // case 2 puts((char [4]){ INITIALIZERS } + i % 2); // case 3 }
In every use of the INITIALIZERS
macro, the variable i
is incremented three times. In cases 1 and 2, there is no undefined behavior, because
the increments are in expressions that are indeterminately sequenced with respect
to one another, not unsequenced. There is no guarantee in what order the evaluations
are done, so there is no guarantee in what order they will appear, but the initial
values are guaranteed to be '0'
, '1'
and '2'
.
(It's not perfectly clear whether that guarantee was provided by C99, which instead said:
The order in which any side effects occur among the initialization list expressions is unspecified.
In any event, as a data point, that guarantee was not honored by GCC until release 4.6, in 2011.)
On the other hand, because case 3 contains an unsequenced evaluation of i
in the same full expression, it has undefined behavior.
Considering the number of hours it took me to finally reach this conclusion, I thought it would be worthwhile to bring it to the full committee to make sure everyone understands and agrees with it. If so, an addition to the rationale might be in order.
Consider 6.7.6p3 (emphasis added):
A full declarator is a declarator that is not part of another declarator. The end of a full declarator is a sequence point. …
Also 6.2.4p8:
A non-lvalue expression with structure or union type, where the structure or union contains a member with array type (including, recursively, members of all contained structures and unions) refers to an object with automatic storage duration and temporary lifetime.36) Its lifetime begins when the expression is evaluated and its initial value is the value of the expression. Its lifetime ends when the evaluation of the containing full expression or full declarator ends. Any attempt to modify an object with temporary lifetime results in undefined behavior.
It is clear from these passages that the sequence of evaluations includes not only full expressions, but also full declarators – whatever sense it makes to talk about “evaluating” a full declarator. But sentence 3 does not acknowledge that reality.
My inclination is to adopt a bit of terminology from Ada, and start talking about the “elaboration” of a declarator, which, for a variably modified type, involves the run-time evaluation of array sizes, and then to re-draft sentence 3 and the other paragraphs cited here, to make it clear that sequence points separate elaborations of full declarators as well as evaluations of full expressions. In any event, I think there's a problem in sentence 3 that needs to be fixed.
Expressions in abstract declarators are not mentioned at all (compare to sentence 1). The logical inference is that such an expression is not a full expression by itself, but part of the containing full expression. But there are cases where there is no containing full expression. For example:
typedef _Atomic(int (*)[rand()]) T; _Alignas(int [rand()]) int i;
In these examples, not only is there no containing full expression, there isn't even any containing full declarator, because these expressions appear in the declaration specifiers, not the declarator.
Probably the simplest approach here would be to disallow variably modified types
with _Atomic
and _Alignas
, at least until the next revision
of the standard.
The list of full expression contexts (sentence 2) is not logically complete. According to the definition (sentence 1), an expression appearing in a constant-expression context is (often) a full expression. Of course there are no sequencing implications relevant for constant expressions, but it's not clear that makes it important for a constant expression not to be counted as a full expression. In any event, it's not clear how the list normatively interacts with the definition.
I think we should consider moving the list into a note, so it's clear that the definition is, well, definitive. The note could also point out that sequencing is irrelevant to constant expressions.
Committee Discussion
Apr 2014 meeting
Committee Discussion
The committee solicits the author for any suggested technical corrigenda.
Oct 2014 meeting
Committee Discussion
There was no paper submitted on this topic, and the committee will again solicit the author for suggested technical corrigenda.
Submitter: Joseph Myers
Submission Date: 2013-07-21
Source: WG 14
Reference Document:
N1730
Subject: Floating-point issues in C11 from PDTS 18661-1 UK review, Issue 1
Summary
Some issues with floating point in C11 have been identified as part of the UK review of the N1711 draft of TS 18661-1. While such issues relate to the general area of C bindings to IEC 60559:2011, and so could be addressed in the TS on that basis, since the issues also apply to C11 as-is it may be more appropriate to address some or all of these issues as Defect Reports with a view to having a normative fix in a future TC to C11 rather than only having a fix in conjunction with the new bindings.
Issue 1: Choice of long double in Annex F
Annex F provides various choices for the long double type (which may or may not be an IEC 60559 type), but no method is provided for an application to determine which choice has been made.
If a macro is provided to say whether the type is an IEC 60559 type, all the other properties can be determined from the existing <float.h> macros. So, a sufficient fix would be:
In 5.2.4.2.2, insert a new paragraph after paragraph 10: Whether a type matches an IEC 60559 type is characterized by the implementation-defined values of FLT_IS_IEC_60559, DBL_IS_IEC_60559, and LDBL_IS_IEC_60559:
- 0 type does not match an IEC 60559 format
- 1 type's values and operations are those of an IEC 60559 basic, interchange or extended type
Committee discussion led to a proposed committee response.Apr 2014 meeting
Correspondence with the author led the committee to augment the proposed committee response.
Proposed Committee Response
To do as suggested, distinguish whether float, double, and long double are IEC or not, requires the addition of new macros, which is a feature, which is not allowed by the mechanism of defect reports.The defect originator notes that the underlying issue that needs consideration in any further comprehensive publication of the Standard is that all implementation defined behaviors need to be strictly called out in the Standard, and moreover that they be done so in a manner that is accessible to a client of the implementation to allow proper choice of algorithms. It has been asserted that leaving implementation defined behaviors formally undefined in the Standard has led to significant and unnecessary divergences in implementations.
Submitter: Joseph Myers
Submission Date: 2013/07/21
Source: WG 14
Reference Document:
N1730
Subject: Floating-point issues in C11 from PDTS 18661-1 UK review, Issue 2
Summary
Some issues with floating point in C11 have been identified as part of the UK review of the N1711 draft of TS 18661-1. While such issues relate to the general area of C bindings to IEC 60559:2011, and so could be addressed in the TS on that basis, since the issues also apply to C11 as-is it may be more appropriate to address some or all of these issues as Defect Reports with a view to having a normative fix in a future TC to C11 rather than only having a fix in conjunction with the new bindings.Issue 2: Definition of FLT_ROUNDS
The C11 definition of FLT_ROUNDS is inadequate in that it refers to floating-point addition but does not say addition of what type. If long double is not an IEC 60559 type, it may not fully support all rounding modes even though they are supported by other types. (This is an actual issue with real implementations using non-IEC 60559 types for long double.) A suitable fix would be:
In 5.2.4.2.2#8, insert "for type float" after "floating-point addition". At the end of F.2#1, insert "The value of FLT_ROUNDS applies to all IEC 60559 types supported by the implementation, but may not apply to non-IEC 60559 types.".
Oct 2014 meeting
Committee Discussion
The Proposed Committee Response was revised for accuracy and more detailed information, and is provided below.Proposed Committee Response
The committee regards the existing definition of FLT_ROUNDS as intended to apply to types float, type double and long double. However, if all three types cannot support the same set of rounding modes, the implementation needs to set FLT_ROUNDS to -1 meaning indeterminable.
As has been pointed out, in Annex F, only the types float and double need be IEC 60559 types. If long double is not an IEC 60559 type (for example, a pair of doubles), it may not support the same set of rounding modes as float and double. In this case, having FLT_ROUNDS apply to float and double (but not long double) would result in a value of 0, 1, 2, or 3 and would provide new and useful information to the programmer.
However, this behavioral change could also break existing programs, and as such the committee prefers to leave as is for this revision of the Standard.
Submitter: Joseph Myers
Submission Date: 2013-07-21
Source: WG 14
Reference Document:
N1730
Subject: Floating-point issues in C11 from PDTS 18661-1 UK review, Issue 3
Summary
Some issues with floating point in C11 have been identified as part of the UK review of the N1711 draft of TS 18661-1. While such issues relate to the general area of C bindings to IEC 60559:2011, and so could be addressed in the TS on that basis, since the issues also apply to C11 as-is it may be more appropriate to address some or all of these issues as Defect Reports with a view to having a normative fix in a future TC to C11 rather than only having a fix in conjunction with the new bindings.Issue 3: Floating-point exceptions and 6.5#5
C11 6.5#5 says "If an exceptional condition occurs during the evaluation of an expression (that is, if the result is not mathematically defined or not in the range of representable values for its type), the behavior is undefined.". When the Annex F bindings are in effect, it must be intended that floating-point exceptions do not produce such undefined behavior (instead, behavior such as evaluating to a NaN must be defined). But no normative text actually says that.
A suitable fix would be:
Append to 6.5#5: For implementations defining __STDC_IEC_559__, this does not apply to exceptional conditions where the behavior (such as raising a floating-point exception and returning a NaN) is defined by Annex F, directly or by reference to IEC 60559.
Committee Discussion
If an exceptional condition occurs during the evaluation of an expression (that is, if the result is not mathematically defined or not in the range of representable values for its type), and the behaviour is not defined in this annex or by reference to IEC 60559, 6.5p5 applies and the behaviour is undefined.
Apr 2014 meeting
Committee Discussion
Further correspondence with the author and excerpted in N1804 has identified the core issue as being a simple misunderstanding of the applicability of normative annexes to the standard.
Proposed Committee Response
WG14 treats normative annexes such as Annex F as if they were linear extensions of the standard itself. When Annex F is requested via definition of __STDC_IEC_559__ then 6.5#5 is superseded and floating point exceptions become well defined.
Submitter: Joseph Myers
Submission Date: 2013-07-21
Source: WG 14
Reference Document:
N1730
Subject: Floating-point issues in C11 from PDTS 18661-1 UK review, Issue 4
Summary
Some issues with floating point in C11 have been identified as part of the UK review of the N1711 draft of TS 18661-1. While such issues relate to the general area of C bindings to IEC 60559:2011, and so could be addressed in the TS on that basis, since the issues also apply to C11 as-is it may be more appropriate to address some or all of these issues as Defect Reports with a view to having a normative fix in a future TC to C11 rather than only having a fix in conjunction with the new bindings.Issue 4: Floating-point state not being an object
The description of the floating-point environment in C11 fails to make sufficiently clear what is or is not an object (C11 footnote 205 is not normative, and so cannot be used to that effect); it uses terms such as "system variable" without saying what that is. Simply moving that footnote to normative text would fix this issue:
Move the contents of footnote 205 (C11 subclause 7.6) to the end of 5.1.2.3#2.
Committee Discussion
Apr 2014 meeting
Committee Discussion
The committee discusses this issue further and could not see an actual defect: there are no misinterpretations stated or implied.
Proposed Committee Response
Since operations on the floating point environment are well defined there is no need to normatively define anything further about its implementation. The footnote adds clarity and should remain as is.
Submitter: Joseph Myers
Submission Date: 2013-07-23
Source: WG 14
Reference Document:
N1731
Subject: Issues with alignment in C11, part 1
Summary
There are various deficiencies in the C11 text about alignment requirements.Issue 1: Existence of over-aligned types
6.2.8#3 defines the concept of an over-aligned type, with a footnote saying "Every over-aligned type is, or contains, a structure or union type with a member to which an extended alignment has been applied.". But there is no way in the syntax to apply such an alignment to a member. _Alignas appears in the syntax for alignment-specifier, which in turn appears in that for declaration-specifiers (6.7#1). But structure and union members instead use struct-declaration which uses specifier-qualifier-list which doesn't include a case for alignment-specifier at all. So for the reference to over-aligned types, and the reference in 6.7.5#6 to the "declared object or member", to be meaningful, something needs adding to the syntax for struct-declaration. (Note that specifier-qualifier-list is also used in the syntax for type-name, and it seems less likely that a type-name was intended to be able to include alignment-specifiers.)
Committee Discussion
- The desired and intended syntax is indeed missing.
- Whereas one could achieve the desired effect by placing the directive on the aggregate itself and controlling it by the maximum alignment of each of its members, this is far from the intended goal, and we do consider this a defect.
- We solicit a suggested technical corrigendum from the author.
- Modifying the definition of type name will require considerable thought.
- Applying the directive beyond aggregate members, such as a simple scalar declaration, is not well defined, and brings in the various storage durations and how or whether all or any are supported.
Apr 2014 meeting
Committee Discussion
specifier-qualifier-alignment-list:type-specifier specifier-qualifier-alignment-list_opt
type-qualifier specifier-qualifier-alignment-list_opt
alignment-specifier specifier-qualifier-alignment-list_opt
Change the use of specifier-qualifier-list in the syntax for struct-declaration to specifier-qualifier-alignment-list.
6.7.2#2:
Change "and in the specifier-qualifier list in each struct declaration and type name" to ", in the specifier-qualifier-alignment list in each struct declaration and in the specifier-qualifier list in each type name".
6.7.3#5:
Change "specifier-qualifier-list" to "specifier-qualifier-list, specifier-qualifier-alignment-list or declaration-specifiers" (twice).(That the wording about duplicate qualifiers and qualifiers used with _Atomic doesn't deal with declaration-specifiers, the syntax production relevant to normal declarations, is a pre-existing problem noticed in the course of preparing this wording.)
(I believe all the semantics and constraints required for alignment specifiers on members are in place, including 6.2.7#1 dealing with cross-translation-unit type compatibility and references to bit-fields and members in 6.7.5.)
Oct 2014 meeting
Committee Discussion
There still has not been adequate review of these changes. The Project Editor and others have been asked to examine these changes closely prior to our next meeting.
Submitter: Joseph Myers
Submission Date: 2013-07-23
Source: WG 14
Reference Document:
N1731
Subject: Issues with alignment in C11, part 2
Summary
There are various deficiencies in the C11 text about alignment requirements.Issue 2: Contexts in which alignments are supported
6.2.8#2 defines "fundamental alignment": "A fundamental alignment is represented by an alignment less than or equal to the greatest alignment supported by the implementation in all contexts, which is equal to _Alignof (max_align_t)."
6.2.8#3 defines "extended alignment": "An extended alignment is represented by an alignment greater than _Alignof (max_align_t). It is implementation-defined whether any extended alignments are supported and the contexts in which they are supported. A type having an extended alignment requirement is an over-aligned type."
6.2.8#4 defines "valid alignment", saying "Alignments are represented as values of the type size_t. Valid alignments include only those values returned by an _Alignof expression for fundamental types, plus an additional implementation-defined set of values, which may be empty. Every valid alignment value shall be a nonnegative integral power of two.".
max_align_t is specified in 7.19#2 as "an object type whose alignment is as great as is supported by the implementation in all contexts".
The memory management functions in 7.22.3 are defined to return a pointer "suitably aligned so that it may be assigned to a pointer to any type of object with a fundamental alignment requirement and then used to access such an object or an array of such objects in the space allocated". In the case of aligned_alloc, there may be a stricter requirement given by the alignment passed to the function, but the alignment passed to the function can't result in memory any less-aligned than a fundamental alignment requirement. The alignment requirement still applies even if the size is too small for any object requiring the given alignment (see the response to C90 DR#075).
There are various problems with the above:
struct s { long double ld; }
has an alignment requirement bigger than long double, it should still be possible to allocate memory for it with malloc, and the same applies to any typedef from a standard header that might also have a bigger alignment requirement than any basic type.
The following principles seem natural for any fix for this issue:
Committee Discussion
Apr 2014 meeting
Committee Discussion
A fundamental alignment is a valid alignment less than or equal to _Alignof (max_align_t). Fundamental alignments shall be supported by the implementation for objects of all storage durations. The alignment requirements of the following types shall be fundamental alignments:
- all atomic, qualified or unqualified basic types;
- all atomic, qualified or unqualified enumerated types;
- all atomic, qualified or unqualified pointer types;
- all array types whose element type has a fundamental alignment requirement;
- all types specified in clause 7 as complete object types;
- all structure or union types all of whose elements have types with fundamental alignment requirements and none of whose elements have an alignment specifier specifying an alignment that is not a fundamental alignment.
In 6.2.8#3, change "the contexts in" to "the storage durations of objects for which".
In 6.2.8#4, change "those values returned by an _Alignof expression for fundamental types" to "fundamental alignments".
(Note that the above admits the possibility that e.g. 1 and 4 are fundamental alignments, but 2 isn't, in which case it can't be an extended alignment either. If we want to ensure that 2 isn't a valid alignment at all in that case, rather than possibly having valid alignments that are neither fundamental nor extended, also change "additional implementation-defined set of values" to "additional implementation-defined set of extended alignments" in 6.2.8#4.) In 6.7.5#3, change "in the context in which it appears" to "for an object of the storage duration, if any, being declared". Add a new constraint at the end of 6.7.5#3: "An object shall not be declared with an over-aligned type with an extended alignment requirement not supported by the implementation for an object of that storage duration.".
(This deals with the point that if an over-aligned struct is defined, then objects with that type may be supported with some storage durations but not others, so it is the declaration of an object with that type that can violate a constraint rather than the declaration of the struct.)
In 7.19#2, change "whose alignment is as great as is supported by the implementation in all contexts" to "whose alignment is the greatest fundamental alignment".
(I'm trying to avoid an undesirable implication that if an alignment of e.g. 1MB is supported in all contexts - and it's quite plausible that an implementation can support more or less arbitrary alignments - then max_align_t must have such an alignment and so such an alignment must count as fundamental and so malloc must return memory suitable aligned for it.)
Oct 2014 meeting
Committee Discussion
The proposed changes have raised no concerns and so the committee has agreed to use them as the following Proposed Technical Corrigendum.
Proposed Technical Corrigendum
Change 6.2.8#2 to:
A fundamental alignment is a valid alignment less than or equal to _Alignof (max_align_t). Fundamental alignments shall be supported by the implementation for objects of all storage durations. The alignment requirements of the following types shall be fundamental alignments:
- all atomic, qualified or unqualified basic types;
- all atomic, qualified or unqualified enumerated types;
- all atomic, qualified or unqualified pointer types;
- all array types whose element type has a fundamental alignment requirement;
- all types specified in clause 7 as complete object types;
- all structure or union types all of whose elements have types with fundamental alignment requirements and none of whose elements have an alignment specifier specifying an alignment that is not a fundamental alignment.
In 6.2.8#3, change
"the contexts in"to
"the storage durations of objects for which".
In 6.2.8#4, change
"those values returned by an _Alignof expression for fundamental types"to
"fundamental alignments".
In 6.7.5#3, change
"in the context in which it appears"to
"for an object of the storage duration, if any, being declared".
Add a new constraint at the end of 6.7.5#3:
"An object shall not be declared with an over-aligned type with an extended alignment requirement not supported by the implementation for an object of that storage duration.".
In 7.19#2, change
"whose alignment is as great as is supported by the implementation in all contexts"to
"whose alignment is the greatest fundamental alignment".
Submitter: Blaine Garst
Submission Date: 2013-07-31
Source: WG 14
Reference Document:
N1736
Subject:Use byte instead of character for memcmp, memcpy
Summary
It has been noted that the descriptions for 7.24.2.1memcpy
, 7.24.2.2 memmove
and 7.24.4.1 memcmp
are written using the term character which is inconsistent with their design as memory functions. Moreover, if one then reads 3.7.2 as allowing character to mean multibyte character, it is thought that confusion could arise as to whether the number of multibyte characters should be supplied rather than the number of bytes.
Although it is clear by 7.24.1 String function conventions paragraph 3
For all functions in this sub clause, each character shall be interpreted as if it had the type unsigned char
that the number of bytes to be used corresponds to the size of a unsigned char
, one has to reference 6.2.6 Representation of types to learn that unsigned char
is in fact a single byte (consisting of CHAR_BIT
bits).
It would be simpler and more to the point if the three memory functions describe their count parameter n
in terms of bytes.
Suggested Technical Corrigendum
memcpy
Change 7.24.2.1 p 2 first sentence from
Thememcpy
function copiesn
characters from the object pointed to bys2
into the object pointed to bys1
.
to
Thememcpy
function copiesn
bytes from the object pointed to bys2
into the object pointed to bys1
.
memmove
Change 7.24.2.2 p 2 from
Thememmove
function copiesn
characters from the object pointed to bys2
into the object pointed to bys1
. Copying takes place as if then
characters from the object pointed to bys2
are first copied into a temporary array ofn
characters that does not overlap the objects pointed to bys1
ands2
, and then then
characters from the temporary array are copied into the object pointed to bys1
.
to
Thememmove
function copiesn
bytes from the object pointed to bys2
into the object pointed to bys1
. Copying takes place as if then
bytes from the object pointed to bys2
are first copied into a temporary array ofn
bytes that does not overlap the objects pointed to bys1
ands2
, and then then
bytes from the temporary array are copied into the object pointed to bys1
.
memcmp
Change 7.24.4.1 p 2 from
Thememcmp
function compares the firstn
characters of the object pointed to bys1
to the firstn
characters of the object pointed to bys2
.
to
Thememcmp
function compares the firstn
bytes of the object pointed to bys1
to the firstn
bytes of the object pointed to bys2
.
Proposed Committee Response
After reviewing the original motivation and suggestion for change, it was noted by the project editor that "character" is used in several distinct contexts, and that it would be inappropriate to simply improve one area without a comprehensive review of all uses such that the existing consistency of uses of character be replaced in a consistent new manner, as yet undetermined. As it stands, although careful reading is strictly required, it is correct and as such this is not a defect.
Submitter: Fred J. Tydeman
Submission Date: 2013-08-20
Source: WG 14
Reference Document:
N1739,
DR 285
Subject: Boolean from complex
Summary
What is the value of: _Bool b = 0.0 + 3.0*I;
I believe that there is a contradiction between 6.3.1.2 Boolean type and 6.3.1.7 Real and complex in that one requires that the value to be 1 and the other requires the value to be 0.
6.3.1.2 Boolean type1 When any scalar value is converted to _Bool, the result is 0 if the value compares equal to 0; otherwise, the result is 1.
6.3.1.7 Real and complex2 When a value of complex type is converted to a real type, the imaginary part of the complex value is discarded and the value of the real part is converted according to the conversion rules for the corresponding real type.
DR 285 against C99 had a similar issue on conversion from imaginary to boolean. That resulted in:
G.4.2 Real and imaginary1 When a value of imaginary type is converted to a real type other than _Bool,376) the result is a positive zero.
376) See 6.3.1.2.
Suggested Technical Corrigendum
I believe that 6.3.1.7 Real and complex, paragraph 2 should be changed to:
2 When a value of complex type is converted to a real type other than _Bool(footnote), the imaginary part of the complex value is discarded and the value of the real part is converted according to the conversion rules for the corresponding real type.(footnote) See 6.3.1.2.
Committee Discussion
The committee agrees.
Proposed Technical Corrigendum
Change 6.3.1.7, paragraph 2 to:
When a value of complex type is converted to a real type other than _Bool(footnote), the imaginary part of the complex value is discarded and the value of the real part is converted according to the conversion rules for the corresponding real type.(footnote) See 6.3.1.2.
Submitter: Fred J. Tydeman
Submission Date: 2013-08-20
Source: WG 14
Reference Document:
N1740,
DR
231, DR
250
Subject: What are the semantics of a
# non-directive?
Summary
What is a directive name? What are the semantics of a # non-directive?
In particular, what should happen for a translation unit with these four lines:
# non-directive
# "Long string"
# 'Many characters are implementation defined'
# 1234
The syntax in 6.10 Preprocessing directives has in group-part:
# non-directive
The C standard section 6.10, paragraph 3 has:
A non-directive shall not begin with any of the directive names appearing in the syntax.
I find that confusing as directive name is only used in the C standard in 6.10 paragraphs 3 and 4 (without any definition). So, what is a directive name?
Assuming directive name is one of:
then my four line program contains lines that are
# non-directive
so should be valid. However, almost every C compiler I have tried considers them errors that end translation. I did find at least one C compiler that ignored them (treated them as comments). I did find at least one C compiler that considered them errors even inside of:
#if 0
#endif
where they should have been ignored.
I believe that gcc treats
# 1234
the same as:
# line 1234
I see no semantics for non-directive. So, what is supposed to happen with them? Is it implicitly undefined?
Since preprocessing directives (which includes non-directive) are deleted at the end of translation phase 4, these non-directives could act as comments.
DRs 231 and 250 appear to contradict each other on what happens with a non-directive and neither refers to the other.
DR 231 Says that text-line and non-directive are not implementation defined. They are placeholders in the phases of translation and are subject to normal processing in subsequent phases of translation. And that words were supposed to be added to the Rationale.
DR 250 Says that non-directive is a preprocessing directive. And, it added that as a footnote in 6.10.3#11
Neither DR added normative words.
In answering this, we should consider what happens with mis-spellings, such as:
Should # non-directive be a comment (and ignored)? Implementation defined? An error that ends translation (like #error)? Undefined behaviour?
Suggested Technical Corrigendum
Since I do not know what should happen, I have none. But, if we decide on undefined behaviour, I would like that as explicit words.
Committee Discussion
There is intentional vagueness in this area such that implementations have and exhibit unspecified and useful additional behaviors. This has been a source of historical confusion and should be addressed.
Oct 2014 meeting
Committee Discussion
A small change was identified and made in the Proposed Technical Corrigendum.Proposed Technical Corrigendum
Add new paragraph 6.10 paragraph 9:
The execution of a non-directive preprocessing directive results in undefined behavior.
Add to Annex J.2:
The execution of a non-directive preprocessing directive (6.10)
Submitter: Douglas Walls
Submission Date: 2013-08-24
Source: WG 14
Reference Document:
N1744
Subject:What is the value of TSS_DTOR_ITERATIONS for implementations with no maximum?
Summary
Suggested Technical Corrigendum
Oct 2013 meeting
Committee Discussion
Committee Discussion
The question posed should be answered.
Proposed Committee Response
The standard intentionally does not define a value of TSS_DTOR_ITERATIONS for implementations with no maximum.The TSS_DTOR_ITERATIONS macro is used to limit recursion at thread termination. The issue is that existing practice allows the creation of new tss bindings during the destructor call, and one destructor might reincarnate the original, thus forming an infinite recursive destructor loop. This loop may appear non-deterministically and is difficult to detect. The purpose of TSS_DTOR_ITERATIONS is as a bound to such recursion.
It is possible monitor the recursion depth with careful defensive programming and in those cases the value of TSS_DTOR_ITERATIONS is useful as that bound.
Submitter: Martin Sebor
Submission Date: 2013-09-02
Source: WG 14
Reference Document:
N1752
Subject: tmpnam_s clears s[0] when
maxsize > RSIZE_MAX
Summary
The majority of bounds checking functions are specified to set the first element of the destination buffer, s[0], to the NUL character when a constraint violation occurs and the s pointer is non-null and the size of the buffer is greater than zero and less than or equal to SIZE_MAX.
However, the tmpnam_s function sets s[0] to NUL even when maxsize is greater than RSIZE_MAX, making its behavior on constraint violation inconsistent with the rest.
Change paragraph 8 in the Returns section of tmpnam_s to read:
- If no suitable string can be generated, or if there is a runtime-constraint violation and s is not null and maxsize is greater than zero and not greater than RSIZE_MAX, the tmpnam_s function sets s[0] to the null character and returns a nonzero value.
Committee Discussion
The committee agrees with the issue, and requests that the suggested technical corrigendum be broken into more parts for both clarity and consistency.
Apr 2014 meeting
Committee Discussion
The committee did not receive revised words and will again solicit them from the author.
Oct 2014 meeting
Committee Discussion
The paper N1873 was provided and discussed, and after several revisions the following proposal were approved.
Proposed Technical Corrigendum
Change K.3.5.1.2 paragraph 8 (the Returns section of tmpnam_s) from:
If no suitable string can be generated, or if there is a runtime-constraint violation, the tmpnam_s function writes a null character to s[0] (only if s is not null and maxsize is greater than zero) and returns a nonzero value.
to:
If no suitable string can be generated, or if there is a runtime-constraint violation, the tmpnam_s function:
- if s is not null and maxsize is both greater than zero and not greater than RSIZE_MAX, writes a null character to s[0]
- returns a nonzero value.
Submitter: Freek Wiedijk and Robbert Krebbers (Radboud University Nijmegen, The Netherlands)
Submission Date: 2013-08-30
Source: WG 14
Reference Document:
N1747
Subject: Instability of uninitialized automatic variables
Summary
The standard is unclear about the following questions:
Specifically, consider:
unsigned char x[1]; /* intentionally uninitialized */ printf("%d\n", x[0]); printf("%d\n", x[0]);
Does the standard allow an implementation to let this code print two different values? And if so, if we insert either of the following three statements
x[0] = x[0]; x[0] += 0; x[0] *= 0;
between the declaration and the printf
statements, is this
behavior still allowed?
Or alternatively, can these printf
statements exhibit undefined
behavior instead of having to print a reasonable number.
The standard is unclear about these questions.
On the one hand the committee response to Defect Report #260 strongly suggests that the committee decided that the standard implies the answer to question 1 to be "yes". (Although Defect Report #260 applies to the C99 standard and hence has been superseded by the C11 standard, no modification to the standard text was deemed necessary at the time, and all relevant text in the C11 standard is identical to that in the C99 standard.) The relevant quote from the committee response to Defect Report #260 is:
In the case of an indeterminate value [...] the actual bit-pattern may change without direct action of the program.
A subtlety is that Defect Report #260 talks about bit-patterns and not about values,
but for variables of type
unsigned char
there is a one-to-one correspondence between
bit-patterns and values.
Another argument in favor of "instability" of indeterminate values is that values can "become indeterminate" (e.g. 5.1.2.3p5, 6.2.4p2, and 6.2.4p6). In these cases the value of an object may also change without an explicit store (and can keep changing?)
On the other hand, 6.7.9p10 states that the kind of uninitialized variables that we are discussing get an indeterminate value. From 3.19.2 it follows that if a type has no trap values, then indeterminate and unspecified values are the same. And in 3.19.3, it is stated explicitly that an unspecified value is chosen. Which implies that the value - after having been chosen - cannot change anymore.
Another argument against "instability" is that 6.8p3 states that "the values are stored in the objects (including storing an indeterminate value in objects without an initializer) each time the declaration is reached in the order of execution", and that 6.2.4p2 states that "An object [...] retains its last-stored value throughout its lifetime." The only way that one could read this in light of Defect Report #260 is if "retaining an indeterminate value" is read as meaning that the indeterminateness of the value is retained, without the value having a specific value.
It seems attractive to make a distinction between indeterminate values that are allowed to change without direct action of the program in the way that Defect Report #260 interpreted the standard, and unspecified values that do not have this property. However the current text of 3.19.2 does not allow for this interpretation. Also, probably some instances of "indeterminate" and "unspecified" would need to be changed for such an interpretation to make sense. (For example in 6.2.6.1p6 "the bytes of the object representation that correspond to any padding bytes take unspecified values." should probably become "... take indeterminate values.")
The reason for question 3 is that if the kind of "instability"
that questions 1 and 2 ask for is allowed to propagate maximally, then it
becomes impossible to implement printf
in C itself.
When converting an indeterminate value to a string of output characters, the
value can keep changing underneath, and the code cannot protect itself
against this.
On the other hand, if library functions exhibit undefined behavior on these kinds
of "unstable" uninitialized values, then an fwrite
of a struct with uninitialized
padding bytes would also give undefined behavior.
The fact that one wants to be able to copy uninitialized padding bytes in
structs using memcpy
without undefined behavior is the reason that using the
value of an uninitialized object is not undefined behavior.
This seems to suggest that an fwrite
of a struct with uninitialized padding
bytes should not exhibit undefined behavior.
We see three reasonable sets of answers to these questions:
Easy to repair the unclarity in the standard. Just add text that explicitly states that indeterminate values cannot change without direct action from the program. This will prevent people from invoking the response to Defect Report #260 from then on.
Restricts the kind of optimizations compilers are allowed to perform.
Specifically, "unstable" values will also propagate through function calls. Also, after
x[0] *= 0;
the value of x[0]
still will be "unstable" and hence
still can be any byte, and will not necessary be 0
.
Gives compilers more freedom to perform optimizations.
Is Defect Report #260-compliant (i.e., "the committee did not change its mind").
Needs more modifications to the text of the standard. It will then be necessary to make an explicit distinction between "indeterminate non-trap value" and "unspecified value".
memcpy
and maybe
fwrite
should be immune from this)Restricts program behaviors least, giving compilers even more freedom.
Needs even more modification to the text of the standard.
Needs a decision on what library functions will not have undefined behavior when working on indeterminate values.
This is certainly not compatible with the current version of the standard, as no undefined behavior of this kind related to library functions is described there.
In 6.2.4p2, change "An object exists, has a constant address, and retains its last-stored value throughout its lifetime." to "An object exists and has a constant address throughout its lifetime. The value of an object is retained, including the object representation, until some other value is stored into it, or until the moment when the value becomes indeterminate (at which moment it is replaced with an indeterminate value, and after which that value is retained again)."
In 3.19.2 change "either an unspecified value or a trap representation" to "either an unspecified value or a trap representation, which can change arbitrarily without direct action from the program".
In 6.2.4p2, change "An object exists, has a constant address, and retains its last-stored value throughout its lifetime." to "An object exists, has a constant address, and retains its last-stored value (provided this value is not indeterminate), throughout its lifetime."
At the end of 6.5p1 add "If at least one of the operands of an operator is indeterminate, the result of the operator is also indeterminate."
Some instances of "indeterminate" and "unspecified" (to be determined) should be replaced by respectively "unspecified" and "indeterminate". See for example the instance in 6.2.6.1p6 mentioned earlier.
The changes for resolution (b), and also:
In 7.1.4p1 add: "If a function is called with an indeterminate value, the behavior is undefined."
In a selection (to be determined) of functions from the library, add text that counters this general statement added to 7.1.4p1.
Committee Discussion
Apr 2014 meeting
Committee Discussion
The author provided N1793 and an accompanying presentation N1818 in which his position changed to believing that "wobbly" values are not actually defined by the standard, and after discussion agreed that the following committee response would be an acceptable resolution.
Proposed Committee Response
Submitter: Shao Miller
Submission Date: 2013-09-29
Source: WG 14
Reference Document:
N1762
Subject: Effective Type in Loop Invariant
Summary
The definition for "effective type" does not appear to apply to non-lvalue expressions. This can cause a behavioural difference, in loops.6.5p6:
The effective type of an object for an access to its stored value is the declared type of the object, if any.87) If a value is stored into an object having no declared type through an lvalue having a type that is not a character type, then the type of the lvalue becomes the effective type of the object for that access and for subsequent accesses that do not modify the stored value. If a value is copied into an object having no declared type using memcpy or memmove, or is copied as an array of character type, then the effective type of the modified object for that access and for subsequent accesses that do not modify the value is the effective type of the object from which the value is copied, if it has one. For all other accesses to an object having no declared type, the effective type of the object is simply the type of the lvalue used for the access.
Given the following code:
union u1 {
int x;
long y;
};
int func1(void) {
union u1 o1 = { 42 };
return (0, o1).x;
}
The o1 sub-expression in the return statement's expression accesses the stored union value of the object. The comma operator's result has that value, but it is not an lvalue and so "effective type" does not appear to apply. While the access to o1 involves an access to a stored value, the membership operator can be said to access an object whose value is available, but perhaps not exactly "stored." o1.x is an lvalue, but (0, o1).x is not.
6.5.2.3p3:
A postfix expression followed by the . operator and an identifier designates a member of a structure or union object. The value is that of the named member,95) and is an lvalue if the first expression is an lvalue. If the first expression has qualified type, the result has the so-qualified version of the type of the designated member.
6.5p7:
An object shall have its stored value accessed only by an lvalue expression that has one of the following types:88)
- a type compatible with the effective type of the object,
- a qualified version of a type compatible with the effective type of the object,
- a type that is the signed or unsigned type corresponding to the effective type of the object,
- a type that is the signed or unsigned type corresponding to a qualified version of the effective type of the object,
- an aggregate or union type that includes one of the aforementioned types among its members (including, recursively, a member of a subaggregate or contained union), or
- a character type.
Given:
union u2 {
int x;
long y;
char ca[2];
};
int func2(void) {
union u2 o2 = { 42 };
return (0, o2).x;
}
We have a similar situation, even though (0, o2) yields an object with temporary lifetime. (Side question: Should the expression (0, o2).ca == o2.ca yield zero, non-zero, or should it be implementation-defined?)
Suppose we have a portable strategy to determine whether or not the object representations of int and long are the same. If they are and if we have the following code:
union u3 {
int x;
long y;
};
long func3(void) {
union u3 o3;
o3.x = 42;
return (0, o3).y;
}
Are we violating the effective type rules? We might expect type-punning to be relevant here and the membership operator to be accessing a member value of a union value.
If the answer is yes, then does the Standard define the effective type of the non-lvalue expression 0, o3 ?
If the answer is no, then this can cause the loss of an optimization opportunity in the following code:
struct s4 {
int x;
float f;
};
void func4(long * lp, struct s4 * s4p) {
int c;
for (c = 0; c < (0, *s4p).i; ++c)
--*lp;
}
We do not expect *lp to alias into *s4p, so we might optimize this loop such that (0, *s4p).i is only computed once. If, in another translation unit, it turned out that these did alias, the optimization would normally be justified based on a violation of the effective type rules. If there isn't a violation because of the non-lvalue nature of the comma operator's expression, then the optimization would not appear to be justified.
Suggested Technical Corrigendum
None.
Committee Discussion
The committee did not have adequate time to consider these issues and intends that these issues be further refined through consultation with the author.
Apr 2014 meeting
Committee Discussion
Further input was not received from the author and will again be solicited.
Oct 2014 meeting
Committee Discussion
Discussion with the author clarified these issues, and the paper N1888 was discussed. From that, we extract the following exampleand question, what is the result of (0,o2).ca == o2.ca?
union u2 { int x; long y; char ca[2]; }; int func2(void) { union u2 o2 = { .ca = "a" };Given that the comma operator doesn't yield an lvalue (6.5.17), and from 6.2.4p8 such a non-lvalue expression is stated to have automatic storage duration, this seems to require that the answer is false, even though this defeats compiler optimizations.
The effective type rule 6.5.p6 also does not seem to apply to objects with temporary lifetime, and has undesirable consequences.
The direction the committee would like to go is something like:
In 6.2.4p8, append
An object with temporary lifetime behaves as if it had the declared type of its value. Such an object is known as a temporary object. A temporary object need not have a unique address.
Submitter: Fred J. Tydeman (USA)
Submission Date: 2013-10-22
Source: WG14
Reference Document: N1776
Subject: Atomic flag type and operations
Summary
It appears to me that there is a wording problem in 7.17.8.*
7.17.8 Atomic flag type and operations
#1: The atomic_flag type provides the classic test-and-set functionality. It has two states, set and clear.7.17.8.1 The atomic_flag_test_and_set functions
#2: Atomically sets the value pointed to by object to true.#3: Atomically, the value of the object immediately before the effects.
7.17.8.2 The atomic_flag_clear functions
#2: Atomically sets the value pointed to by object to false.
An issue is states (set, clear) versus values (true, false).
Does an atomic_flag structure have both states (set, clear) and values (true, false)? Can it have all four combinations?
Another issue is 'set' is used both as a verb and a noun.
Another issue is: While the test is atomic, and the set is atomic, it is not clear that both test and set are part of the same atomic operation.
I have been told that the same issues exists in the C++ standard (29.7 [atomics.flag]).
There was discussion of these topics on the WG14 reflector (around messages 13067 to 13073)
One person in the discussion would like the value zero (from default static initialization) to be the clear state. They also mentioned DR 421.
Based upon the email discussion, the intent was that flags logically have exactly two states: "set" and "clear". The test_and_set operation returns true if it was "set", and false if it was "clear". Test_and_set sets the state to "set", and the clear operations set the state to "clear". The value zero need not be the "clear" state.
Suggested Technical Corrigendum
Replace
7.17.8.1 The atomic_flag_test_and_set functions
#2: Atomically sets the value pointed to by object to true.#3: Atomically, the value of the object immediately before the effects.
7.17.8.2 The atomic_flag_clear functions
#2: Atomically sets the value pointed to by object to false.
with:
7.17.8.1 The atomic_flag_test_and_set functions
#2: Tests the state of the flag pointed to by object and then sets the flag, as a single atomic operation.#3: Returns true if the flag was set when tested or false otherwise.
7.17.8.2 The atomic_flag_clear functions
#2: Atomically clears the flag pointed to by object.
Add to the rationale in the section on atomic flag:
The atomic flag type is defined in terms of states, not values, as the value zero (false) need not be the "clear" state. The committee knows of one implementation where zero is the "set" state.
Apr 2014 meeting
Committee Discussion
Oct 2014 meeting
Committee Discussion
The paper N1853 was provided and discussed, revised, discussed further, and a further paper was solicited. In particular, the committee did not like "converted to a _Bool" because it implies some unspecified arithmetic conversion.
Submitter: Fred J. Tydeman (USA)
Submission Date: 2013-10-22
Source: WG14
Reference Document: N1777
Subject: ATOMIC_VAR_INIT (issues 3 and 4)
Related: DR
422 and DR
427
Summary
I see several issues with ATOMIC_VAR_INIT. They could be turned into one combined defect report, or separate defects, or folded into DR 422.
Consider the following code:
#include <stdatomic.h>
int main(void){
atomic_int guide1 = ATOMIC_VAR_INIT(42); /* known value(42); WHAT STATE? */
atomic_int guide2; /* indeterminate value; indeterminate state */
atomic_int guide3 = 42; /* known value(42); indeterminate state */
static atomic_int guide4; /* known value(0); valid state */
static atomic_int guide5 = 42; /* known value(42); valid state */
atomic_int guide6;
atomic_init(&guide6, 42); /* known value(42); initialized state */
return 0;
}
What is the status of the additional state carried for guide1?
Is the state of guide1 the same as what guide6 has? If yes, does "initialization-compatible" mean do the same thing as if atomic_init() of the same object with the same value?
(Issue 3 from N1777)
ATOMIC_VAR_INIT is not usable in assignment to an atomic object.I see no difference between:
atomic_int guide = ATOMIC_VAR_INIT(42);
and
atomic_int guide; guide = ATOMIC_VAR_INIT(42);
I would hope that initialization (which looks like an assignment in a declaration) and a simple assignment would be equivalent and ATOMIC_VAR_INIT could be used in either context.
(Issue 4 from N1777)
What should happen if ATOMIC_VAR_INIT(value) is used in context other than initializing an atomic object of the same type as the value?Should it be undefined behaviour? A constraint violation? Just the value value converted to the type of the object?
atomic_float f = ATOMIC_VAR_INIT(42); /* type mis-match */ int nonAtomic = ATOMIC_VAR_INIT(42); /* non-atomic object */ if( ATOMIC_VAR_INIT(42) ){...}; guide1 = 1729 + ATOMIC_VAR_INIT(42) * 3; void func( atomic_int ai ); /* function parm/arg */ func( ATOMIC_VAR_INIT(42) ); /* DR 427 is now making this initialization (not assigment) */
DR 427 is changing how a function parameter is getting its value from the actual argument from assignment to initialization (to get around const). Would this initialization be a valid context for ATOMIC_VAR_INIT?
Suggested Technical Corrigendum
In the first sentence of 7.17.2.1#2, after
suitable for initializing
add the words
or assigning to
Add to 7.17.2.1 as a constraint or a new paragraph between 3 and 4:
If ATOMIC_VAR_INIT is used in a context other than initialization [or assignment] of an atomic object of a compatible type of the value, the behaviour is undefined.
Proposed Committee Response
The ATOMIC_VAR_INIT macro prepares an atomic value that includes any extra state necessary for a non-lock-free type. Initialization, by definition, ignores all previous state. Assignment must honor the extra state that would indicate another atomic operation in progress; such an assignment takes the non-atomic corresponding value resulting from removing all qualifiers including atomic from the value expression, and will manipulate the extra state held in the object to assure proper atomic assignment semantics. ATOMIC_VAR_INIT produces a value appropriate for initialization because it will have any necessary extra state, whereas a value suitable for assignment is the non-qualified version of the assignment expression.All uses of ATOMIC_VAR_INIT other than for initialization result in implicitly undefined behavior.
Submitter: Fred J. Tydeman (USA)
Submission Date: 2013-10-22
Source: WG14
Reference Document: N1777
Subject: ATOMIC_VAR_INIT issue 5
Summary
I see several issues with ATOMIC_VAR_INIT. They could be turned into one combined defect report, or separate defects, or folded into DR 422.
Consider the following code:
#include <stdatomic.h>
int main(void){
atomic_int guide1 = ATOMIC_VAR_INIT(42); /* known value(42); WHAT STATE? */
atomic_int guide2; /* indeterminate value; indeterminate state */
atomic_int guide3 = 42; /* known value(42); indeterminate state */
static atomic_int guide4; /* known value(0); valid state */
static atomic_int guide5 = 42; /* known value(42); valid state */
atomic_int guide6;
atomic_init(&guide6, 42); /* known value(42); initialized state */
return 0;
}
What is the status of the additional state carried for guide1?
Is the state of guide1 the same as what guide6 has? If yes, does "initialization-compatible" mean do the same thing as if atomic_init() of the same object with the same value?
(Issue 5 from N1777)
Zero initialization of static atomic objects in C requires more than in C++.I have been told that C's 7.17.2.1#2:
...; however, the default (zero) initialization for objects with static or thread-local storage duration is guaranteed to produce a valid state.
is not in C++. If true and assuming that the two languages should be the "same" here, should this be deleted from C? Added to C++?
DR 422 is somewhat related to this issue.
Suggested Technical Corrigendum
No suggestion on the requirements mis-match between C and C++.
Apr 2014 meeting
Committee Discussion
Oct 2014 meeting
Committee Discussion
There were was no substantiative further discussion.
Proposed Committee Response
Interoperability with C++ atomics must be done by macros that use C++'s declarative syntax for atomic variables. As such there is no direct compatibility issue as is asserted, and 7.17.2.1#2 shall remain.
Submitter: Rajan Bhakta
Submission Date: 2014-03-05
Source: WG14
Reference Document: N1798
Subject: Compile time definition of UINTN_C(value)
Summary
With reference to ISO/IEC WG14 N1569, subclause 7.20.4.1: The macro UINTN_C(value)
shall expand to an integer constant expression
corresponding to the type uint_leastN_t
.
7.20.4 p1 imposes a stricter requirement on the form of the expansion; it must be an integer constant (for which paragraph 2 points to 6.4.4.1).
The type described in 7.20.4 p3 for the result of the expansion has an interesting property; we observe this for uint_least16_t
without reference to
the UINT16_C
macro by using u'\0'
in a context where it will be first promoted as part of the usual arithmetic conversions:
#include <assert.h>
#if u'\0' - 1 < 0
// Types: #if (uint_least16_t) - (signed int) < (signed int)
// Due to 6.10.1 p4, near the reference to footnote 167,
// after applying the integer promotions as part of 6.3.1.8 p1
// to the operands of the subtraction, the expression becomes:
// Types: #if (unsigned int) - (signed int) < (signed int)
// Following 6.3.1.8 p1 through to the last point gives:
// Types: #if (unsigned int) - (unsigned int) < (signed int)
// Result: false
# error Expected large unsigned value.
#endif
int main(void) {
// Types: assert((uint_least16_t) - (signed int) < (signed int))
// Assuming that signed int can represent all values of uint_least16_t,
// after applying the integer promotions as part of 6.3.1.8 p1
// to the operands of the subtraction, the expression becomes:
// Types: assert((signed int) - (signed int) < (signed int))
// Result: true
assert(u'\0' - 1 < 0);
return 0;
}
The code presented should neither fail to compile nor abort when executed (for example) on a system using two's complement and 8, 16 and 32 bits
(respectively) for char
, short
and int
with no padding bits.
Consider the case for N = 8 or 16 on systems with INT_MAX
as +2147483647, UCHAR_MAX
as 255 and USHRT_MAX
as 65535: it is unclear how a macro can be
formed such that it expands to an integer constant that has the promoted signed int type in phase 7 of translation and also the promoted unsigned int
type in phase 4 of translation without special (non-standard) support from the compiler.
Even if the requirement for an integer constant is relaxed to only require an integer constant expression, the case for N = 8 on systems with INT_MAX
as +32767 and UCHAR_MAX
as 255 remains a problem without the use of casts (since uint_least16_t
, for which we can form a literal, has different promotion behaviour from uint_least8_t
).
Implementations seen:
#define UINT8_C(c) c ## U
#define UINT8_C(c) c
DR 209 seemed to try to address the issue of needing special compiler support in order to define the macros for integer constants; however, the problem seems to remain.
Suggested Technical Corrigendum
UINT{8,16}_C
macros from the standard.Apr 2014 meeting
Committee Discussion
Oct 2014 meeting
Proposed Committee Response
The committee believes that DR209 is still appropriate in that "compiler magic" must be used for the implementation of these macros. The committee does not consider this a defect.As such, both proposed resolutions were found inappropriate.
Submitter: David Keaton (suggested by Jens Gustedt)
Submission Date: 2014-03-13
Source: WG14
Reference Document: N1802
Subject: The ctime_s
function in Annex K defined incorrectly
Summary
The ctime_s
function in Annex K was defined
analogously to ctime
, and some of the text from the
definition of ctime
was copied and modified
slightly.
K.3.8.2.2p4 states that ctime_s
is equivalent to
the following.
asctime_s(s, maxsize, localtime_s(timer))
In this case, the text from the original ctime
definition was not quite modified enough.
The localtime_s
function takes two arguments and the
above code only supplies one.
Suggested Technical Corrigendum
In K.3.8.2.2p4, replace
asctime_s(s, maxsize, localtime_s(timer))
with the following.
asctime_s(s, maxsize, localtime_s(timer, &(struct tm){ 0 }))
Proposed Technical Corrigendum
In K.3.8.2.2p4, replace
asctime_s(s, maxsize, localtime_s(timer))
with the following.
asctime_s(s, maxsize, localtime_s(timer, &(struct tm){ 0 }))
Submitter: Martin Sebor
Submission Date: 2014-03-18
Source: WG14
Reference Document: N1806
Subject: ATOMIC_XXX_LOCK_FREE macros not constant expressions
Summary
Section 7.17.1 Introduction (to section 7.17 Atomics
Section 7.17.5 Lock-free property, then goes on to specify that the atomic lock-free macros (presumably the same ones as those listed in 7.17.1) expand to one of three values: 0, 1, or 2.
Neither of the two sections above, nor any other in the standard, specifies whether or not the macros are required to expand to constant expressions usable in preprocessor #if directives. This is in contrast to some other standard macros such as those defined in <limits.h> which are typically so specified using language such as:
The values given below shall be replaced by constant expressions suitable for use in #if preprocessing directives.
As discussed in the thread starting with SC22WG14.13216, the only purpose for the existence of the ATOMIC_XXX_LOCK_FREE macros is to be able to write more efficient code by relying on their use in preprocessor #if conditionals. Thus, the absence of the requirement that they expand to constant expressions makes the macros unsuitable for that purpose.
Suggested Technical CorrigendumIn section 7.17.1, modify paragraph 3 as indicated below:
...which expand to constant expressions suitable for use in #if preprocessing directives and which indicate the lock-free property of the corresponding atomic types (both signed and unsigned); and
Proposed Technical Corrigendum
In section 7.17.1 paragraph 3 change:
...which indicate the lock-free property of the corresponding atomic types (both signed and unsigned); andto
...which expand to constant expressions suitable for use in #if preprocessing directives and which indicate the lock-free property of the corresponding atomic types (both signed and unsigned); and
Submitter: Martin Sebor
Submission Date: 2014-03-22
Source: WG14
Reference Document: N1807
Subject: atomic_load missing const qualifier
Summary
The synopsis of the atomic_load pair of generic functions specified in 7.17.7.2 shows that they accept pointers to a volatile- (bot not const-) qualified type:
#include <stdatomic.h> C atomic_load(volatile A *object); C atomic_load_explicit(volatile A *object, memory_order order);
The absence of the const qualifier implies that the functions cannot be called with an argument of type const A* since there is no such conversion.
However, since neither function modifies its argument, there is no need to prevent it from being called with an argument of type const A*. And, in fact, the latest draft C++ standard as of this writing, N3936, does provide an overload of each function that takes a const volatile pointer.
Suggested Technical CorrigendumIn section 7.17.7.2, paragraph 1, Synopsis, modify the declarations of the atomic_load pair of generic functions as indicated below:
#include <stdatomic.h> C atomic_load(const volatile A *object); C atomic_load_explicit(const volatile A *object, memory_order order);
Proposed Technical Corrigendum
In section 7.17.7.2, paragraph 1, Synopsis, modify the declarations of the atomic_load pair of generic functions from:
to:#include <stdatomic.h> C atomic_load(volatile A *object); C atomic_load_explicit(volatile A *object, memory_order order);
#include <stdatomic.h> C atomic_load(const volatile A *object); C atomic_load_explicit(const volatile A *object, memory_order order);
Submitter: Martin Sebor
Submission Date: 2014-03-22
Source: WG14
Reference Document: N1808
Subject: aligned_alloc underspecified
Summary
The aligned_alloc function specifies the following constraints on its arguments, alignment and size:
The value of alignment shall be a valid alignment supported by the implementation and the value of size shall be an integral multiple of alignment.
Therefore, the behavior of the function is undefined when either constraint is violated.
According to section 6.2.8, paragraph 1, the greatest alignment a conforming implementation is required to support (known as fundamental alignment) is _Alignof(max_align_t). Furthermore, according to paragraph 2 of the same section, whether alignments greater than the fundamental alignment (known as extended alignments) are supported and in what contexts is implementation-defined.
The standard specifies no mechanism by which programs could determine whether an extended alignment is supported by an implementation, or whether the aligned_alloc function is among the contexts where an extended alignment is supported.
As a result, there is no way for strictly conforming programs to use the aligned_alloc function with an alignment argument greater than the result of _Alignof(max_align_t). Since the malloc function returns objects that meet the same alignment requirement, this restriction makes aligned_alloc useless in portable programs.
This restriction is unnecessary since it's possible, and in fact nearly trivial given access to the internal details of the memory allocator, to implement an efficient aligned_function that fails when its arguments don't meet the specified requirements.
As a data point, the POSIX Advanced Realtime function posix_memalign, as well as the historical BSD memalign function, are both required to return a null pointer when either of their arguments don't meet the specified requirements (in addition to setting errno to EINVAL.
Suggested Technical CorrigendumThe proposed corrigendum below changes the standard to require aligned_alloc to fail by returning a null pointer when either of its constraints is violated.
In section 7.22.3.1, modify paragraph 2 as indicated below:
The aligned_alloc function allocates space for an object whose alignment is specified by alignment, whose size is specified by size, and whose value is indeterminate.TIf the value of alignmentshall beis not a valid alignment supported by the implementationandor the value of sizeshall beis not an integral multiple of alignment the function shall fail by returning a null pointer.
In addition, in section J.2 Undefined behavior, remove the following bullet:
— The alignment requested of the aligned_alloc function is not valid or not supported by the implementation, or the size requested is not an integral multiple of the alignment (7.22.3.1).
If the proposal above isn't acceptable, then an alternative solution to consider that would allow aligned_alloc to be used even in strictly conforming programs is to add a new function to determine whether a given alignment is supported by an implementation. For example:
_Bool alignment_is_valid (size_t alignment);
Returns
The alignment_is_valid function returns non-zero if the value specified by alignment is a valid alignment argument to the aligned_alloc function, and zero otherwise.
Apr 2014 meeting
Committee Discussion
The committee agrees that the first proposal in the suggested technical corrigendum should be adopted.
Proposed Technical Corrigendum
In section 7.22.3.1, change paragraph 2 from:
The aligned_alloc function allocates space for an object whose alignment is specified by alignment, whose size is specified by size, and whose value is indeterminate. The value of alignment shall be a valid alignment supported by the implementation and the value of size shall be an integral multiple of alignment.
The aligned_alloc function allocates space for an object whose alignment is specified by alignment, whose size is specified by size, and whose value is indeterminate. If the value of alignment is not a valid alignment supported by the implementation or the value of size is not an integral multiple of alignment the function shall fail by returning a null pointer.
In addition, in section J.2 Undefined behavior, remove the following bullet:
— The alignment requested of the aligned_alloc function is not valid or not supported by the implementation, or the size requested is not an integral multiple of the alignment (7.22.3.1).
Submitter: Martin Sebor
Submission Date: 2014-03-25
Source: WG14
Reference Document: N1812
Subject: problems with references to objects in signal handlers
Summary
We believe there are two problems in section 7.14.1.1 The signal function, paragraph 5, which specifies the constraints under which signal handlers can access objects declared in other scopes. The problems are summarized in the following two subsections. The section titled Suggested Technical Corrigendum then proposes a correction to both.
Section 7.14.1.1 The signal function, paragraph 5, specifies the following constraints. Note, in particular, to use of the word "refers," and the reference to objects with "static or thread storage duration" underscored in the text below.
If the signal occurs other than as the result of calling the abort or raise function, the behavior is undefined if the signal handler refers to any object with static or thread storage duration that is not a lock-free atomic object other than by assigning a value to an object declared as volatile sig_atomic_t, or the signal handler calls any function in the standard library other than the abort function, the _Exit function, the quick_exit function, or the signal function with the first argument equal to the signal number corresponding to the signal that caused the invocation of the handler.Underspecification of referring to objects
The standard doesn't formally define the term refer but its uses in the text suggest that it denotes any use of an object, including one that doesn't involve accessing it. The term access is defined in 3.1 to mean an <execution-time action> to read or modify the value of an object.
Preventing signal handlers from accessing objects is necessary in order to avoid data races between accesses (reads and writes) to the same object in the rest of the program that are in progress but not completed at the time the signal is delivered.
However, by making use of the word "refers," the sentence in 7.14.1.1 quoted above implies that even mentioning the name of an object in an unevaluated context such as the sizeof expression, or taking its address is undefined in a signal handler. This restriction is unnecessary, since such references are safe because they cannot introduce any sort of a data race between the signal handler and the rest of the program. Thus, referring to such objects without accessing them should be permitted in conforming programs.
Furthermore, accessing a const object to read (but not modify) its value also cannot introduce a data race and is safe as well. Thus, the restriction can be relaxed even further to allow signal handlers to read constant objects. Note that const objects are those that are declared const. In particular, accessing an object that was not declared const via a pointer to a const-qualified type does not change the fact that the object itself is not const. This distinction is important to understand that relaxing this constraint cannot introduce the potential for a data race when such a non-const object is modied in the program while it's accessed via a const-qualified pointer in a signal handler.
The comments in the following example should make this distinction clear:
Missing restriction to access other functions' local objectsconst int safe = (1 << SIGINT) | (1 << SIGQUIT); int unsafe = (1 << SIGHUP) | (1 << SIGTERM); volatile sig_atomic_t sigcount [2]; void handler (int signo) { const int *pmask; // pointer to const int // taking the address of any object is safe and should be allowed pmask = &safe; // access to safe should be allowed since it's a const object if ((1 << signo) & *pmask) ++sigcount [0]; // safe and should be allowed pmask = &unsafe; // access to unsafe remains undefined since it's not a const object if ((1 << signo) & *pmask) ++sigcount [1]; }
The sentence from paragraph 5 quoted above specifically singles out objects with static or thread storage duration, but permits signal handlers to access objects with automatic storage duration without a similar restriction. However, a signal handler that has access to a local variable defined in another function whose execution is interrupted by the delivery of a signal resulting in the invocation of the signal handler contains the same potential data race as if the two functions both accessed the same object with static storage duration.
To make clear how this condition could arise, consider the following program which, when atomic_intptr_t is a lock-free type, is strictly conforming according to the letter of the standard despite the data race.
Suggested Technical Corrigendumatomic_intptr_t p; // assume atomic_intptr_t is lock-free void handler (int signo) { // the following write access should be undefined since it modifies // an object with automatic storage duration declared in f ++*(int*)p; } void f (void) { int i = 0; p = (atomic_intptr_t)&i; signal (SIGINT, handler); while (i < 7) printf ("%i\n", i); }
The proposed corrigendum below changes the standard to remove the unnecessary constraints discussed above, and to add the missing restriction to prevent accessing local variables defined elsewhere in the program. The reference to the lifetime of auto objects makes sure that accesses to local variables defined in signal handlers themselves as well as in functions called from them remain well defined.
In section 7.14.1.1, modify the first sentence of paragraph 5 as indicated below:
If the signal occurs other than as the result of calling the abort or raise function, the behavior is undefined if the signal handlerrefers toaccesses any non-const object with static or thread storage duration, or any non-const object with automatic storage duration whose lifetime started before the signal handler has been entered, that is not a lock-free atomic object other than by...
In addition, make the corresponding change to section J.2 Undefined behavior.
Apr 2014 meeting
Committee Discussion
Oct 2014 meeting
Committee Discussion
The paper N1874 was submitted and discussed, again, as a defect, rather than as a new proposal, and the suggested changes to allow new behavior were again rejected. It was noted that a const volatile object implemented in hardware, such as random number generator, might not provide a consistent value if accessed from a signal handler, and so there was general agreement that any changes in this area warrant very careful consideration.
Proposed Committee Response
Extending the behavior as requested is a feature and appropriate as input to the next revision of this Standard. It was noted that a const volatile object that might seem acceptable to reference from a signal handler might not be if it were implemented in hardware (e.g. a hardware random number generator).
Submitter: Robert Seacord
Submission Date: 2014-03-25
Source: WG14
Reference Document: N1813
Subject: Clarifying objects accessed in signal handlers
Summary
It appears the intent of the committee in Subclause 5.1.2.3 paragraph 5 was to allow lock-free atomic objects or objects of type volatile sig_atomic_t to be accessed from a signal handler. Objects of type atomic_flag are an obvious choice operations on an object of type atomic_flag are required to be lock free. However, objects of type atomic_flag can only be meaningfully accessed by a call to a function, and calls to these functions from a signal handler are undefined behavior according to subclause 7.14.1.1 paragraph 5.
Change subclause 7.14.1.1 paragraph 5 from:
If the signal occurs other than as the result of calling the abort or raise function, the behavior is undefined if the signal handler refers to any object with static or thread storage duration that is not a lock-free atomic object other than by assigning a value to an object declared as volatile sig_atomic_t, or the signal handler calls any function in the standard library other than the abort function, the _Exit function, the quick_exit function, or the signal function with the first argument equal to the signal number corresponding to the signal that caused the invocation of the handler. Furthermore, if such a call to the signal function results in a SIG_ERR return, the value of errno is indeterminate.252)
to:
If the signal occurs other than as the result of calling the abort or raise function, the behavior is undefined if the signal handler refers to any object with static or thread storage duration that is not a lock-free atomic object other than by assigning a value to an object declared as volatile sig_atomic_t, or the signal handler calls any function in the standard library other than the abort function, the _Exit function, the quick_exit function, the atomic_flag_test_and_set functions, the atomic_flag_clear functions, or the signal function with the first argument equal to the signal number corresponding to the signal that caused the invocation of the handler. Furthermore, if such a call to the signal function results in a SIG_ERR return, the value of errno is indeterminate.252)
Sublcause J.2 Undefined behavior. Page 566
Change:
A signal occurs other than as the result of calling the abort or raise function, and the signal handler refers to an object with static or thread storage duration that is not a lock-free atomic object other than by assigning a value to an object declared as volatile sig_atomic_t, or calls any function in the standard library other than the abort function, the _Exit function, the quick_exit function, or the signal function (for the same signal number) (7.14.1.1).
to:
A signal occurs other than as the result of calling the abort or raise function, and the signal handler refers to an object with static or thread storage duration that is not a lock-free atomic object other than by assigning a value to an object declared as volatile sig_atomic_t, or calls any function in the standard library other than the abort function, the _Exit function, the quick_exit function, the atomic_flag_test_and_set functions, the atomic_flag_clear functions, or the signal function (for the same signal number) (7.14.1.1).
Apr 2014 meeting
Committee Discussion
The Suggested Technical Corrigendum was accepted as the Proposed Technical Corrigendum.
Oct 2014 meeting
Committee Discussion
Upon further consideration, since by implication 5.1.2.3p5 allows by implication any of the atomic functions on lock-free atomic objects, the following revision to the Suggested Technical Corrigendum was substantially adopted from the new paper N1887
Proposed Technical Corrigendum
Change subclause 7.14.1.1 paragraph 5 from:
If the signal occurs other than as the result of calling the abort or raise function, the behavior is undefined if the signal handler refers to any object with static or thread storage duration that is not a lock-free atomic object other than by assigning a value to an object declared as volatile sig_atomic_t, or the signal handler calls any function in the standard library other than the abort function, the _Exit function, the quick_exit function, or the signal function with the first argument equal to the signal number corresponding to the signal that caused the invocation of the handler. Furthermore, if such a call to the signal function results in a SIG_ERR return, the value of errno is indeterminate.252)
to:
If the signal occurs other than as the result of calling the abort or raise function, the behavior is undefined if the signal handler refers to any object with static or thread storage duration that is not a lock-free atomic object other than by assigning a value to an object declared as volatile sig_atomic_t, or the signal handler calls any function in the standard library other than
the abort function,
the _Exit function,
the quick_exit function,
the atomic functions from stdatomic.h, when the atomic arguments are lock-free,
the atomic_is_lock_free function with any atomic argument,
or the signal function with the first argument equal to the signal number corresponding to the signal that caused the invocation of the handler. Furthermore, if such a call to the signal function results in a SIG_ERR return, the value of errno is indeterminate.252)
In subclause J.2 Undefined behavior, change:
A signal occurs other than as the result of calling the abort or raise function, and the signal handler refers to an object with static or thread storage duration that is not a lock-free atomic object other than by assigning a value to an object declared as volatile sig_atomic_t, or calls any function in the standard library other than the abort function, the _Exit function, the quick_exit function, or the signal function (for the same signal number) (7.14.1.1).
to:
A signal occurs other than as the result of calling the abort or raise function, and the signal handler refers to an object with static or thread storage duration that is not a lock-free atomic object other than by assigning a value to an object declared as volatile sig_atomic_t, or calls any function in the standard library other than the abort function, the _Exit function, the quick_exit function, the atomic functions from stdatomic.h (when the atomic arguments are lock-free) , the atomic_is_lock_free function with any atomic argument, or the signal function (for the same signal number) (7.14.1.1).
Submitter: Aaron Ballman
Submission Date: 2014-04-02
Source: WG14
Reference Document: N1817
Subject: Left-shifting into the sign bit
Summary
Harmonizing left-shift with C++14It is not uncommon to see code such as:
signed someint_t min_value = 1 << (CHAR_BIT * sizeof(someint_t));
However, left-shifting a one bit into the sign bit is undefined behavior, despite the fact that the majority of (twos-complement) architectures handle it properly.
Suggested Technical Corrigendum
The result ofC++ addressed this in C++14 with DR1457 with identical wording modifications.E1 << E2
isE1
left-shiftedE2
bit positions; vacated bits are filled with zeros. IfE1
has an unsigned type, the value of the result isE1 x 2E2
, reduced modulo one more than the maximum value representable in the result type. IfE1
has a signed type and nonnegative value, andE1 x 2E2
is representable in the corresponding unsigned type of the result type, then that value, converted to the result type, is the resulting value; otherwise, the behavior is undefined.
Apr 2014 meeting
Proposed Committee Response
This is not a defect.The committee will track this and consider it for the next revision of the standard.
Submitter: David Keaton (suggested by Max Woodbury)
Submission Date: 2014-06-27
Source: WG14
Reference Document: N1842
Subject: Clarifying the Behavior of the #line Directive
Summary
Context:
In a distributed development environment, the exact file name
passed to the compiler or preprocessor may vary from site to site. It
is therefore desirable to be able to set the file name as seen by
__FILE__
and elsewhere to a uniform value. The mechanism
to do this is the '#line <num> "<string>"
'
form of the '#line
' preprocessor directive. It is also
necessary that such a directive leave the line numbering sequence
unchanged. Further, it is desirable that edits that change the
location of the directive in the source module should not require
modification to the directive and that comments embedded in the
directive likewise do not have to be accounted for.
Searches of the online literature show that a directive of the
form '#line __LINE__ "string"
' is expected to have this
property.
Despite this, at least one compiler/preprocessor does not allow this.
Technical argument:
The value substituted for the predefined macro
'__LINE__
' is specified in 6.10.8.1p1 as the presumed
line number of the current source line. The presumed line number is
initially (6.10.4p2) the number of newline characters (or their
equivalent) seen in phase 1 of the translation process, plus 1, at
the time of substitution. (Note that this is not the
same as the time of tokenization, which is where the failing compilers
make their mistake.) The mechanism for transferring this count
between phase 1 and phase 4, where macro substitution takes place, is
not specified, but may be presumed to exist and be reliable. (If it
were not, the __LINE__
predefined macro would be
useless.) That makes the question 'when does the substitution take
place?'
Macro substitution in directives is a separate issue from macro expansion in code. It does not always take place. If and when it occurs depends on the directive and the details of its form. That means the entire directive has to be 'in hand' in order to be evaluated, and that means, in turn, that the newline that terminates the directive has to have been seen. The standard goes to some length to specify the various directive forms and all include the terminating newline in their specification.
Therefore, when a substitution is made for
'__LINE__
', its value should be the line count following
the end of the directive, which is the same as the line number of next
line in the source module. This is precisely the value that produces
the desired property of the '#line __LINE__ "string"
'
directive.
Correction requested:
While there is no need to change the standard's normative text, a
note that '#line __LINE__ "string"
' and similar
directives leaves line numbering unchanged would both be educational
and make misinterpretations more difficult.
Suggested Technical Corrigendum
Append the following to footnote 177 in 6.10.8.1p1:
#line __LINE__ "newfilename"
changes the presumed
file name without changing the presumed line number.
Oct 2014 meeting
Committee Discussion
The committee discussed the Proposed Technical Corrigendum from N1842 and found that it didn't sufficiently clarify the issue. Investigation during the meeting revealed that several (in fact all that were tested) compilers did not seem to follow the interpretation of the standard as given in N1842, and that it would be best to acknowledge this as unspecified behavior.
Proposed Committee Response
6.10.4 paragraph 2 states that “The line number of the current source line is one greater than the number of new-line characters read or introduced in translation phase 1 (5.1.1.2) while processing the source file to the current token.” Note that it does not say the number of new-line characters that exist prior to the current token; it says the number of new-line characters that have been read while processing to the current token.
In the case of the #line directive of the form
#line pp-tokens new-line
there are two possible values for the number of new-line characters that have been read when processing begins on the first pp-token. In a one-pass preprocessor, the line number at the first pp-token will be the number of new-line characters that exist prior to the #line directive, because that number of new-lines will have been read. In a preprocessor that must see the entire directive before processing it, since the directive explicitly includes a new-line, the line number at the first pp-token will be the number of new-line characters that exist prior to the #line directive plus one.
Therefore, in a #line directive of the form
#line __LINE__ “filename”
there are two possible values for __LINE__, which leads to two possible values for the line number following the #line directive. Both are valid.
Proposed Technical Corrigendum
Add the following
footnote to the end of 6.10.4 paragraph 5.
Because
a new-line is explicitly included as part of the
#line
directive, the number of new-line characters read while processing to
the first
pp-token
may be different depending on whether or not the implementation uses
a one-pass preprocessor. Therefore, there are two possible values
for the line number following a directive of the form
#line
__LINE__ new-line
.
Add the following to J.1
Unspecified behavior.
The
line number following a directive of the form
#line
__LINE__ new-line
(6.10.4).
Submitter: David Keaton (suggested by Hans Boehm)
Submission Date: 2014-07-14
Source: WG14
Reference Document: N1847
Subject: Fixing an inconsistency in atomic_is_lock_free
Summary
The C committee intended to adopt the same model for atomics as C++ to ensure compatibility. Somewhere along the way, there was an error in synchronizing with the C++ atomic model. This could have serious consequences for code that needs to share atomic objects between modules written in C and modules written in C++ (for example, in the case of libraries written in one language being used by a program written in the other).
The C++ standard states the following in 29.4p2.
The function atomic_is_lock_free
(29.6)
indicates whether the object is lock-free. In any given program
execution, the result of the lock-free query shall be consistent for
all pointers of the same type.
However, the C standard states the following in 7.17.5.1p3.
The atomic_is_lock_free
generic function
returns nonzero (true) if and only if the object's operations are
lock-free. The result of a lock-free query on one object cannot
be inferred from the result of a lock-free query on another
object.
The primary issue is compatibility. Secondarily, if the
lock-free property for a given pointer type can change after an
algorithm starts, then atomic_is_lock_free
cannot be used
to select an algorithm in advance if the algorithm will allocate new
atomic objects. The C++ model is therefore more useful.
The error in synchronizing with C++ should be fixed by correcting
the behavior of atomic_is_lock_free
to be the same in C
as in C++.
Suggested Technical Corrigendum
Replace the following sentence from 7.17.5.1p3
The result of a lock-free query on one object cannot be inferred from the result of a lock-free query on another object.
with the following.
In any given program execution, the result of the lock-free query shall be consistent for all pointers of the same type.
Oct 2014 meeting
Committee Discussion
The Suggested Technical Corrigendum needed revision, and new words were crafted and adopted. One consequence from this change that a NULL pointer is now a valid argument.
Proposed Technical Corrigendum
Change 7.17.5.1 paragraph 2 from:
The atomic_is_lock_free generic function indicates whether or not the object pointed to by obj is lock-free.
to:
The atomic_is_lock_free generic function indicates whether or not atomic operations on objects of the type pointed to by obj are lock-free.
Change 7.17.5.1 paragraph 3 from:
The atomic_is_lock_free generic function returns nonzero (true) if and only if the object's operations are lock-free. The result of a lock-free query on one object cannot be inferred from the result of a lock-free query on another object.
to:
The atomic_is_lock_free generic function returns nonzero (true) if and only if atomic operations on objects of the type pointed to by the argument are lock-free. In any given program execution, the result of the lock-free query shall be consistent for all pointers of the same type.
Submitter: Martin Sebor
Submission Date: 2014-09-19
Source: WG14
Reference Document: N1865
Subject: scope of a for loop control declaration
Summary
The scope of a for loop control declaration in C is different from that in C++. In particluar, while in C the declaration establishes its own scope in which the scope of the body of the for statement is nested, in C++ the two are one and the same. The practical implication of this difference is that while in C a declaration in the body can hide the for loop declaration, in C++ such a re-declaration would be ill-formed. The following example demonstrates the difference:
static inline int f (void) { for (int i = 0; ; ) { long i = 1; // valid C, invalid C++ // ... return i; // (perhaps unexpectedly) returns 1 in C } }
During a discussion of this difference on the mailing list (starting with post C22WG14.13355), it was noted that the re-declaration could lead to subtle bugs.
The incompatibility between rules used by the two languages also makes writing headers intended to be used by both C and C++ that contain inline functions more prone to error than necessary.
In addition, it was noted (by Larry Jones in SC22WG14.13359) that the intent was for C99, where the ability to declare a for loop control variable was first added, to follow the C++ rules, but that it had been missed that the C++ rules ultimately adopted by ISO/IEC 14882:1998 changed from those of The Annotated C++ Reference Manual that was initially used to craft the C rules.
The author recognizes that changing the C rules could render some existing programs invalid. However, it is likely that such programs are broken/buggy and thus a breaking change would result in correcting such latent bugs.
Therefore, the proposed corrigendum suggests to align the C rules with those of C++ by adding a new paragraph to section 6.2.1 Scopes of identifiers as follows.
Names declared in clause-1 of the for statement are local to the for statement and shall not be redeclared in a subsequent condition of that statement nor in the outermost block of the controlled statement.
Note: the text of the paragraph is aligned with the corresponding paragraph 4 of section 3.3.3 Block scope of ISO/IEC 14882:2014 (and section 3.3.2 Block scope of ISO/IEC 14882:1998).
Oct 2014 meeting
Committee Discussion
The committee accepted this as a DR because there was an intent to not be gratuitously different than C++, and yet this small drift occurred.Proposed Committee Response
This small and unintended difference between the two languages is known and some of its uses were discussed. It also turns out that some C++ compilers also know and allow this construct with a warning. Overall, the committee concludes that this is not an area we wish to change.
Submitter: Fred J. Tydeman
Submission Date: 2014-09-26
Source: WG14
Reference Document: N1870,
N1871
Subject: maximum representable finite description vs math
Summary
formula for maximum representable finite (normalized) floating-point numbers in 5.2.4.2.2#12, and epsilon floating-point numbers in 5.2.4.2.2#13.Details
The math formula is for a normalized number, while the words are missing 'normalized'. Now, in the floating-point model in paragraph 2, the maximum finite number is the same as the maximum finite normalized number, so it did not matter.
However, if long double is a pair of doubles (not matching the model in paragraph 2), then there can be finite numbers larger than the largest normalized finite number. The largest normalized finite number is DBL_MAX*(1.+DBL_EPSILON/2.), while the largest finite number can be DBL_MAX*2.
Also, if long double is a pair of doubles (not matching the model in paragraph 2), then 'least value greater than 1 that is representable in the given floating point type' is (for double) 1.0+DBL_TRUE_MIN. That makes the difference DBL_TRUE_MIN, which is not the same at the math formula (b to the power (1-p)).
Suggested Technical Corrigendum
In 5.2.4.2.2#13, add 'normalized' between 'least and 'value.
In 5.2.4.2.2#12, add 'normalized' between 'finite' and 'floating-point'.
Add a new paragraph:
12b The values given in the following list shall be replaced by constant expressions with implementation-defined values that are greater than or equal to those shown:
-- maximum representable finite floating-point number (footnote),
(footnote): Need not be normalized.
Oct 2014 meeting
Committee Discussion
The committee accepts the correction of "normalized" but concludes that adding the suggested macros is a feature and out of scope for a DR.
Proposed Technical Corrigendum
In 5.2.4.2.2#12, first item change the phrase
maximum representable finite floating-point number,to
maximum representable finite normalized floating-point number,
In 5.2.4.2.2#13, first item change the phrase
and the least value greater than 1to
and the least normalized value greater than 1
Submitter: Martin Sebor
Submission Date: 2014-09-19
Source: WG14
Reference Document: N1872
Subject: strncpy_s clobbers buffer past null
Summary
K.3.7.1.4, p5 permits strncpy_s to "clobber" characters in the destination buffer past the terminating null:
All elements following the terminating null character (if any) written by strncpy_s in the array of s1max characters pointed to by s1 take unspecified values when strncpy_s returns. 420)Footnote 420 explains that the intent is to allow implementations to copy characters from s2 to s1 while simultaneously checking if any of those characters are null. Such an approach might write a character to every element of s1 before discovering that the first element should be set to the null character.
This intent is to allow efficient implementations to make a single pass over the source sequence that simultaneously copies characters and checks the runtime constraints. (Otherwise two passes would be required, one to compute the length of the source sequence and another to copy it.)
It has been pointed out that the implementation latitude granted by this text goes too far, since the function only might need to write past the null after a constraint violation. Otherwise, when all runtime constraints are satisfied, the function stops copying characters after either the first null is encountered or all n characters have been copied.
Since the mention of unspecified values tends to raise security concerns about information leakage, and since permitting the implementations to modify the contents of the destination buffer past the terminating null on success serves no useful purpose, the requirements on the function can and should be tightened up.
The proposed corrigendum below tightens up the requirements on the function so as to leave intact the contents of the destination buffer past the terminating null on success, while allowing it to clobber its contents on runtime constraint violation.
Modify K.3.7.1.4, p5 as indicated below:
All elements following the terminating null character (if any) written by strncpy_s in the array of s1max characters pointed to by s1 take unspecified values when strncpy_s returns a non-zero value. 420)
Oct 2014 meeting
Proposed Technical Corrigendum
Change K.3.7.1.4, p5 from
All elements following the terminating null character (if any) written by strncpy_s in the array of s1max characters pointed to by s1 take unspecified values when strncpy_s returns. 420)to
All elements following the terminating null character (if any) written by strncpy_s in the array of s1max characters pointed to by s1 take unspecified values when strncpy_s returns a non-zero value. 420)
Submitter: Torvald Riegel
Submission Date: 2014-10-07
Source: WG14
Reference Document: N1881
Subject: lock ownership vs. thread termination
Summary
If a mutex M is acquired by a thread T, and afterwards T terminates without releasing ownership of M, then the resulting state after termination of T seems to be unspecified.
Specifically, N1570 7.26.4.5p2 states:
The mtx_trylock function endeavors to lock the mutex pointed to by mtx. If the mutex is already locked, the function returns without blocking.
However, there is no statement about whether a mutex whose owner has terminated remains locked. This seems to be a source of confusion, and it affects implementations. C++11 specifies that such a case results in undefined behavior (see 30.4.1.2.1p5). On the other hand, POSIX wants (PThreads) mutexes to remain locked in this case (see Austin Group Bug 755).
From an implementation perspective, the C++11 semantics are more practical because they do not require implementations to maintain identities of threads that do not exist any more. For example, with C++11 semantics, an implementation can just use a thread ID to identify an owner, even if another thread eventually reuses the same ID (e.g., a process ID) after the former owning thread terminated. In contrast, the POSIX semantics require an implementation to avoid ABA issues on the thread identities (i.e., the same value representing different states of ownership). This effectively results in a higher runtime overhead for lock acquisition or for lock initialization of at least recursive mutexes, or address space leakage (or other workarounds).
Suggested Technical Corrigendum
I would like the expected behavior to be explicitly specified. To me, C should do what C++11 states. In particular, add the following or a similar sentence at an appropriate place:
The behavior of a program is undefined if a thread terminates while owning a mutex.
Oct 2014 meeting
Committee Discussion
The committee is sympathetic to this concern. A review uncovered the possible need to further specify the behavior of a recursive mutex. A new paper was solicited to discuss this and other issues and their proposed resolutions.
Submitter: Torvald Riegel, Hans Boehm
Submission Date: 2014-10-10
Source: WG14
Reference Document: N1882
Subject: mtx_trylock should be allowed to fail spuriously
Summary
C11 does not appear to allow mtx_trylock
to fail spuriously (i.e., return thrd_busy
even thought the lock was not acquired, yet eventually acquire the lock if it is not acquired by any thread), but C++11 does (see 30.4.1.1/16):
An implementation may fail to obtain the lock even if it is not held by any other thread. [ Note: This spurious failure is normally uncommon, but allows interesting implementations based on a simple compare and exchange (Clause 29). -- end note ] An implementation should ensure that try_lock() does not consistently return false in the absence of contending mutex acquisitions.
It might be better to point out explicitly that programmers should treat mtx_trylock
as if spurious failure were allowed, since the memory model is intentionally too weak to support correct reasoning that is based on a return value of thrd_busy
. There has been debate on this issue, and we would prefer the standard to be clearer. Consider the following example:
Thread 1: v1 = 1; mtx_lock(l1); Thread 2: r1 = mtx_trylock(l1); while (r1 == thrd_success /* was unlocked */) { unlock(l1); r1 = mtx_trylock(l1); } r2 = v1; out(r2);
This program is not data-race-free according to C11, independently of whether mtx_trylock
is allowed to fail spuriously or not; the happens-before-based definition of a data race and the current specification of synchronizes-with relations between mutex operations makes it clear that the program above has a data race on v1
.
However, if spurious failures are not allowed, an intuitive understanding of the memory model in the sense that everything will appear to be sequentially consistent if only locks are used to synchronize does not hold anymore. The intuitive understanding would make the program above correct; in particular the store to v1
by the first thread would be expected to "happen before" the load from v1
by the second thread.
Therefore, to make an intuitive understanding of the C11 memory model and locks match the actual specification, it would be helpful to point out that programmers should assume mtx_trylock
to fail spuriously. Otherwise, without spurious failure, we have cases like the example above in which two operations race according to the specification in spite of the fact that they intuitively can't execute at the same
time.
Allowing spurious failures does not affect the typical uses of mtx_trylock
, for example to acquire several locks without risk of deadlock. It does rule out uses like the example above, however, in which locks are attempted to be used as a replacement for atomics.
(Note that we are not arguing for specifying that mtx_lock
should synchronize with a mtx_trylock
that returns thrd_busy
. This would make the implementation of lock acquisition less efficient on architectures such as ARM or PowerPC. In particular, an atomic_compare_exchange
or similar that transitions the lock's state from not acquired to acquired would have to use memory_order_acq_rel
instead of memory_order_acquire
.)
Suggested Technical Corrigendum
It seems that the normative specification already states the preferred semantics, although the return value specification for thrd_busy
may make readers believe that this return code allows one to infer a certain ordering (see the example above).
We propose to add a clarifying note at an appropriate place (e.g., in 7.26.4.5p3):
Programmers should treatmtx_trylock
as if spurious failures were allowed; the memory model is intentionally too weak to support reasoning based on a return value ofthrd_busy
.
Oct 2014 meeting
Committee Discussion
A spurious failure can occur on PPC/ARM style architectures if, after the load-word-and-reserve instruction is issued the operating system schedules the task out, and upon resumption the corresponding store-word fails because the reservation is lost, even if the lock is unlocked. This failure can be seen by mtx_trylock if it is implemented with atomic_compare_exchange_weak where this failure can occur.A new paper was solicited to extract the corresponding words from C++ so as to keep the two standards as close as possible in this area.
Submitter: Fred Tydeman
Submission Date: 2014-10-28
Source: WG14
Reference Document: N1886
Subject: Complex math functions cacosh and ctanh
Summary
Complex math functions (cacosh (G.6.2.1) and ctanh(G.6.2.6)) are incorrectly specified.Reasons: Mathematically, cacosh(0.0+I*y) = asinh(y) + I*pi/2. Also, C requires cacos(0+I*NaN) to be pi/2+I*NAN, which along with the mathematically identity cacosh(z) = +/-I * cacos(z), means cacosh(0.0 + I*NaN) is NaN + I*pi/2.
Reason for above two: Since ctanh(x+I*y) = (sinh(2x) + I*sin(2y)) / (cosh(2x) + cos(2y)), for any rational number y, cos(2y) cannot be exactly -1, so no 0/(1+(-1)),so no 0/0, so no NaN for the real component of the result
Suggested Technical Corrigendum
Add to G.6.2.1 cacosh before 4th bullet: cacosh(0.0+I*NaN) returns NaN + I*pi/2
Add to G.6.2.1 cacosh 4th bullet: "non-zero" so it reads: cacosh(x + iNaN) returns NaN + i*NaN and optionally raises the ''invalid'' floating-point exception, for finite non-zero x.
Add to G.6.2.6 ctanh before 3rd bullet: ctanh(0.0+I*INF) returns 0.0+I*NAN and raises the ''invalid'' floating-point exception.
Add to G.6.2.6 ctanh 3rd bullet: "non-zero" so it reads: ctanh(x + I*INF) returns NaN + i*NaN and raises the ''invalid'' floating-point exception, for finite non-zero x.
Add to G.6.2.6 ctanh before 4th bullet: ctanh(0.0+I*NaN) returns 0.0+I*NAN
Add to G.6.2.6 ctanh 4th bullet: "non-zero" so it reads: ctanh(x + I*NAN) returns NaN + i*NaN and optionally raises the ''invalid'' floating-point exception, for finite non-zero x.
Oct 2014 meeting
Committee Discussion
This DR is derived from N1867. The committee agrees with the Suggested Technical Corrigendum.
Proposed Technical Corrigendum
Add new paragraph to G.6.2.1 cacosh before 4th bullet:
cacosh(0.0+iNaN) returns NaN + iπ/2
Change G.6.2.1 cacosh 4th bullet from:
cacosh(x + iNaN) returns NaN + iNaN and optionally raises the “invalid” floating-point exception, for finite x.to
cacosh(x + iNaN) returns NaN + iNaN and optionally raises the “invalid” floating-point exception, for finite non-zero x.
Add new paragraph to G.6.2.6 ctanh before 3rd bullet:
ctanh(0.0+i∞) returns 0.0+iNAN and raises the “invalid” floating-point exception.
Change G.6.2.6 ctanh 3rd bullet clause from:
ctanh(x + i∞) returns NaN + iNaN and raises the “invalid” floating-point exception, for finite x.to
ctanh(x + i∞) returns NaN + iNaN and raises the “invalid” floating-point exception, for finite non-zero x.
Add new paragraph to G.6.2.6 ctanh before 4th bullet:
ctanh(0.0+iNaN) returns 0.0+iNAN
Change G.6.2.6 ctanh 4th bullet from:
ctanh(x + iNAN) returns NaN + iNaN and optionally raises the “invalid” floating-point exception, for finite x.to
ctanh(x + iNAN) returns NaN + iNaN and optionally raises the “invalid” floating-point exception, for finite non-zero x.