Defect Report Summary for C11
Version 1.8

Date: April 2015
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 04/2015 Open
DR 407 SC fences do not restrict modification order enough 04/2015 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/2015 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 04/2015 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? 04/2015 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/2015 Open
DR 438 ungetc / ungetwc and file position after discarding push back 04/2015 Closed
DR 439 Issues with the definition of “full expression” 04/2015 Open
DR 440 Floating-point issues in C11 from PDTS 18661-1 UK review, Issue 1 04/2015 Closed
DR 441 Floating-point issues in C11 from PDTS 18661-1 UK review, Issue 2 04/2015 Open
DR 442 Floating-point issues in C11 from PDTS 18661-1 UK review, Issue 3 04/2015 Closed
DR 443 Floating-point issues in C11 from PDTS 18661-1 UK review, Issue 4 04/2015 Closed
DR 444 Issues with alignment in C11, part 1 04/2015 Open
DR 445 Issues with alignment in C11, part 2 04/2015 Review
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? 04/2015 Review
DR 449 What is the value of TSS_DTOR_ITERATIONS for implementations with no maximum? 04/2015 Closed
DR 450 tmpnam_s clears s[0] 04/2015 Review
DR 451 Instability of uninitialized automatic variables 04/2015 Closed
DR 452 Effective Type in Loop Invariant 04/2015 Open
DR 453 Atomic flag type and operations 04/2015 Open
DR 454 ATOMIC_VAR_INIT (issues 3 and 4) 04/2015 Closed
DR 455 ATOMIC_VAR_INIT issue 5 04/2015 Review
DR 456 Compile time definition of UINTN_C(value) 04/2015 Review
DR 457 The ctime_s function in Annex K defined incorrectly 04/2015 Closed
DR 458 ATOMIC_XXX_LOCK_FREE macros not constant expressions 04/2015 Closed
DR 459 atomic_load missing const qualifier 04/2015 Closed
DR 460 aligned_alloc underspecified 04/2015 Closed
DR 461 problems with references to objects in signal handlers 04/2015 Review
DR 462 Clarifying objects accessed in signal handlers 04/2015 Open
DR 463 Left-shifting into the sign bit 04/2015 Closed
DR 464 Clarifying the Behavior of the #line Directive 04/2015 Review
DR 465 Fixing an inconsistency in atomic_is_lock_free 04/2015 Review
DR 466 scope of a for loop control declaration 04/2015 Review
DR 467 maximum representable finite description vs math 04/2015 Review
DR 468 strncpy_s clobbers buffer past null 04/2015 Review
DR 469 lock ownership vs. thread termination 04/2015 Open
DR 470 mtx_trylock should be allowed to fail spuriously 04/2015 Open
DR 471 Complex math functions cacosh and ctanh 04/2015 Review
DR 472 Introduction to complex arithmetic in 7.3.1p3 wrong due to CMPLX 04/2015 Open
DR 473 "A range error occurs if x is too large." is misleading 04/2015 Open
DR 474 NOTE 1 Clarification for atomic_compare_exchange 04/2015 Open
DR 475 Misleading Atomic library references to atomic types 04/2015 Open

DR 400

DR 463 Prev <— Closed —> Next DR 401, or summary at top


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).

AIX
realloc(NULL,0) always returns NULL, errno is EINVAL
realloc(ptr,0) always returns NULL, ptr freed, errno is EINVAL
BSD
realloc(NULL,0) only gives NULL on alloc failure, errno is ENOMEM
realloc(ptr,0) only gives NULL on alloc failure, ptr unchanged, errno is ENOMEM
glibc
realloc(NULL,0) only gives NULL on alloc failure, errno is ENOMEM
realloc(ptr,0) always returns NULL, ptr freed, errno unchanged

The 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 to free, and not by means of a realoc(p, 0).

Oct 2011 meeting

Committee Discussion

Feb 2012 meeting

Committee Discussion

Proposed Technical Corrigendum

In subsection 7.22.3 paragraph 1, change
If the size of the space requested is zero, the behavior is implementation-defined: either a null pointer is returned, ...
to
If 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 (The realloc function), change the final sentence of paragraph 3 from
If memory for the new object cannot be allocated, the old object is not deallocated and its value is unchanged.
to
If size is non-zero and memory for the new object is not allocated, the old object is not deallocated. If size 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.
In subsection 7.22.3.5 (The realloc function), change paragraph 4 from
The 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 could not be allocated.
to
The 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.
Add to subsection 7.31.12 a new paragraph (paragraph 2):
Invoking realloc with a size argument equal to zero is an obsolescent feature.

DR 463 Prev <— Closed —> Next DR 401, or summary at top



DR 401

DR 400 Prev <— Closed —> Next DR 402, or summary at top


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.


Oct 2011 meeting

Committee Discussion

Feb 2012 meeting

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.

DR 400 Prev <— Closed —> Next DR 402, or summary at top



DR 402

DR 401 Prev <— Closed —> Next DR 403, or summary at top


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.
Oct 2011 meeting

Committee Discussion

Feb 2012 meeting

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.

DR 401 Prev <— Closed —> Next DR 403, or summary at top



DR 403

DR 402 Prev <— Closed —> Next DR 404, or summary at top


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.
Oct 2011 meeting

Committee discussion

Feb 2012 meeting

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.

DR 402 Prev <— Closed —> Next DR 404, or summary at top



DR 404

DR 403 Prev <— Closed —> Next DR 405, or summary at top


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.
Oct 2011 meeting

Committee discussion

Feb 2012

Proposed Technical Corrigendum

In 7.17.3 Paragraph 13, remove footnote 256.

DR 403 Prev <— Closed —> Next DR 405, or summary at top



DR 405

DR 404 Prev <— Closed —> Next DR 408, or summary at top


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.
Oct 2011 meeting

Committee discussion

Feb 2012 meeting

Committee Discussion

Oct 2012 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.

NOTE: This total order can be viewed as the modification order of the mutex.

Apr 2013 meeting

Committee Discussion

Accept wording from Oct 2012 as proposed technical corrigendum

Proposed 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.

DR 404 Prev <— Closed —> Next DR 408, or summary at top



DR 406

DR 475 Prev <— Open —> Next DR 407, or summary at top


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 below
If 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.
Oct 2011 meeting

Committee Discussion

Feb 2012 meeting

Committee Discussion

Oct 2012 meeting

Committee Discussion

This item has become WG21 Core issue 1466
Apr 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:

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.
  1. 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 some operation in the visible sequence of M with respect to B side 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 given 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 below. —end note]

  2. Change 5.1.2.4 paragraph 24 as follows:

    [Note: The visible sequence of side effects value 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]

  3. 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]

  4. 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 B that is not memory_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 B that is not memory_order_seq_cst.

    [Note:...

Apr 2015 meeting

Committee Discussion

The provided words were accepted, with slight editorial changes, as the Proposed Technical Corrigendum.

Proposed Technical Corrigendum

Change 5.1.2.4 paragraph 22 from:

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.

to:

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.

After 5.1.2.4 paragraph 22 add:

Note The set of side effects from which a given evaluation might take its value is also restricted by the rest of the rules described here, and in particular, by the coherence requirements below.

Change 5.1.2.4 paragraph 24 from:

Note 11: The visible sequence of side effects depends on the “happens before” relation, which in turn 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.

to

Note 11: The value observed by a load of an atomic depends on the “happens before” relation, which in turn depends on the values observed by loads of atomics. 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.

Change 5.1.2.4 paragraph 27 from:

Note 13: 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” rules.
to
Note 13: 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 coherence requirements.

Change 7.17.3 paragraph 6 from:

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:

to:

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:

DR 475 Prev <— Open —> Next DR 407, or summary at top



DR 407

DR 406 Prev <— Open —> Next DR 423, or summary at top


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.
Oct 2011 meeting

Committee Discussion

Feb 2012 meeting

Committee Discussion

Oct 2012 meeting

Committee Discussion

Oct 2013 meeting

Committee Discussion

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.
  1. [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".]

  2. 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:

    • 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.

    [ 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 ]

Apr 2015 meeting

Committee Discussion

The provided words were adopted as the Proposed Technical Corrigendum. The project editor is asked to review and replace the phrase "before an operation B on M" by "before a modification B of M".

Proposed Technical Corrigendum

Change 7.17.3 paragraph 11 from:

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.

to:

For atomic modifications A and B of an atomic object M, B occurs later than A in the modification order of M if:

DR 406 Prev <— Open —> Next DR 423, or summary at top



DR 408

DR 405 Prev <— Closed —> Next DR 409, or summary at top


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


Oct 2011 meeting

Committee Discussion

Feb 2012 meeting

Committee Discussion

Oct 2012 meeting

Proposed Committee Response

DR 405 Prev <— Closed —> Next DR 409, or summary at top



DR 409

DR 408 Prev <— Closed —> Next DR 410, or summary at top


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

  1. 7.12.5.4 The cosh functions

    Change to: A range error occurs if the magnitude of finite x is too large.

  2. 7.12.5.5 The sinh functions

    Change to: A range error occurs if the magnitude of finite x is too large.

  3. 7.12.6.1 The exp functions

    Change to: A range error occurs if the magnitude of finite x is too large.

  4. 7.12.6.2 The exp2 functions

    Change to: A range error occurs if the magnitude of finite x is too large.

  5. 7.12.6.3 The expm1 functions

    Change to: A range error occurs if the magnitude of finite x is too large.

  6. 7.12.6.6 The ldexp functions

    Change to: A range error may occur for finite arguments.

  7. 7.12.6.13 The scalbn and scalbln functions

    Change to: A range error may occur for finite arguments.

  8. 7.12.7.3 The hypot functions

    Change to: A range error may occur for finite arguments.

  9. 7.12.7.4 The pow functions

    Change to: A range error may occur for finite arguments.

  10. 7.12.8.2 The erfc functions

    Change to: A range error occurs if finite x is too large.

  11. 7.12.8.3 The lgamma functions

    Change to: A range error occurs if finite x is too large.

  12. 7.12.8.4 The tgamma functions

    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.

  13. 7.12.12.1 The fdim functions

    Change to: A range error may occur for finite arguments.

  14. 7.12.13.1 The fma functions

    Change to: A range error may occur for finite arguments.


Feb 2012 meeting

Committee Discussion

Oct 2012 meeting

Committee Discussion

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.

DR 408 Prev <— Closed —> Next DR 410, or summary at top



DR 410

DR 409 Prev <— Closed —> Next DR 412, or summary at top


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.

  1. 7.12.6.5 The ilogb functions:

    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.


Feb 2012 meeting

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.


DR 409 Prev <— Closed —> Next DR 412, or summary at top



DR 411

DR 411 Prev <— Published —> Next DR 411, or summary at top


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.

Feb 2012 meeting

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)


DR 411 Prev <— Published —> Next DR 411, or summary at top



DR 412

DR 410 Prev <— Closed —> Next DR 413, or summary at top


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.

Feb 2012 meeting

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.


DR 410 Prev <— Closed —> Next DR 413, or summary at top



DR 413

DR 412 Prev <— Closed —> Next DR 414, or summary at top


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?

The relevant clause from the standard is 6.7.9 clause 19:
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


Feb 2012 Meeting

Committee Discussion

Oct 2012 meeting

Committee Discussion

Apr 2013 meeting

Committee Discussion

  • There 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.
  • Oct 2013 meeting

    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.


    DR 412 Prev <— Closed —> Next DR 414, or summary at top



    DR 414

    DR 413 Prev <— Closed —> Next DR 415, or summary at top


    Submitter: Tom Plum
    Submission Date: 2012-03-20
    Source: WG14
    Reference Document: N1614
    Subject: Typos in 6.27 Threads <threads.h>

    Summary

    In 7.26.1 paragraph 5
    The enumeration constants are
    mtx_plain
    which is passed to mtx_init to create a mutex object that supports neither timeout nor test and return;
    the "test and return" is referring to try_lock, try_lock is not optional, therefore the "test and return" should be removed.

    In 7.26.4.2 paragraph 2
    The mtx_init function creates a mutex object with properties indicated by type,
    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.
    There are not six values listed, "six" should be changed to "these".

    Suggested Technical Corrigendum

    Change 7.26.1 paragraph 5 to
    The enumeration constants are
    mtx_plain
    which is passed to mtx_init to create a mutex object that does not support timeout;
    Change 7.26.4.2 paragraph 2 to
    The mtx_init function creates a mutex object with properties indicated by type,
    which must have one of the these values:

    Oct 2012 meeting

    Committee Discussion

    Adopt the Suggested Technical Corregendum as proposed.

    Proposed Technical Corrigendum

    Change 7.26.1 paragraph 5 to
    The enumeration constants are
    mtx_plain
    which is passed to mtx_init to create a mutex object that does not support timeout;
    Change 7.26.4.2 paragraph 2 to
    The mtx_init function creates a mutex object with properties indicated by type,
    which must have one of the these values:


    DR 413 Prev <— Closed —> Next DR 415, or summary at top



    DR 415

    DR 414 Prev <— Closed —> Next DR 416, or summary at top


    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 quotient a/b is not representable, the behavior of both a/b and a%b is undefined (6.5.5).

    Oct 2012 meeting

    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 quotient a/b is not representable, the behavior of both a/b and a%b is undefined (6.5.5).


    DR 414 Prev <— Closed —> Next DR 416, or summary at top



    DR 416

    DR 415 Prev <— Closed —> Next DR 417, or summary at top


    Submitter: Owen Shepherd
    Submission Date: 2012-08-12
    Source: WG14
    Reference Document: N1627
    Subject: tss_t destruction unspecified

    Summary

    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)

    Suggested Technical Corrigendum

    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.


    Oct 2012 meeting

    Committee Discussion

    Apr 2013 meeting

    Committee Discussion

    Oct 2013 meeting

    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.

    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.

    After 7.26.6.1 paragraph 2, add the following new paragraphs:
    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.

    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.

    In 7.26.6.2 paragraph 2, add the following new second sentence:
    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.

    Calling tss_delete will not result in the invocation of any destructors.

    In 7.26.6.3 paragraph 2, add the following new second sentence:
    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:

    A call to tss_set 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.4 paragraph 2, add the following new paragraph:
    This action will not invoke the destructor associated with the key on the value being replaced.


    DR 415 Prev <— Closed —> Next DR 417, or summary at top



    DR 417

    DR 416 Prev <— Closed —> Next DR 418, or summary at top


    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 the calloc, malloc, and realloc functions (7.22.3).

    — The amount of storage allocated by a successful call to the calloc, malloc, or realloc function when 0 bytes was requested (7.22.3).
    The following undefined behavior bullet is incomplete in Annex J.2, aligned_alloc() is missing.
    — A non-null pointer returned by a call to the calloc, malloc, or realloc function with a zero requested size is used to access an object (7.22.3).
    The following implementation-defined behavior bullet is incomplete in Annex J.3.12, aligned_alloc() is missing.
    — Whether the calloc, malloc, and realloc 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 the calloc, malloc, realloc, and aligned_alloc functions (7.22.3).
    Change bullet 43 of J.1 to:
    — The amount of storage allocated by a successful call to the calloc, malloc, realloc, or aligned_alloc function when 0 bytes was requested (7.22.3).
    Change bullet 166 of J.2 to
    — A non-null pointer returned by a call to the calloc, malloc, realloc, or aligned_alloc function with a zero requested size is used to access an object (7.22.3).
    Change bullet 37 of J.3.12 to
    — Whether the calloc, malloc, realloc and aligned_alloc functions return a null pointer or a pointer to an allocated object when the size requested is zero (7.22.3).

    Oct 2012 meeting

    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 the calloc, malloc, realloc, and aligned_alloc functions (7.22.3).
    Change bullet 43 of J.1 to:
    — The amount of storage allocated by a successful call to the calloc, malloc, realloc, or aligned_alloc function when 0 bytes was requested (7.22.3).
    Change bullet 166 of J.2 to
    — A non-null pointer returned by a call to the calloc, malloc, realloc, or aligned_alloc function with a zero requested size is used to access an object (7.22.3).
    Change bullet 37 of J.3.12 to
    — Whether the calloc, malloc, realloc and aligned_alloc functions return a null pointer or a pointer to an allocated object when the size requested is zero (7.22.3).


    DR 416 Prev <— Closed —> Next DR 418, or summary at top



    DR 418

    DR 417 Prev <— Closed —> Next DR 419, or summary at top


    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:

    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:

    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:

    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:

    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:

    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.


    Oct 2012 meeting

    Committee Discussion

    Proposed Committee Response

    The consensus was to do nothing and the author agrees.


    DR 417 Prev <— Closed —> Next DR 419, or summary at top



    DR 419

    DR 418 Prev <— Closed —> Next DR 420, or summary at top


    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.


    Oct 2012 meeting

    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)


    DR 418 Prev <— Closed —> Next DR 420, or summary at top



    DR 420

    DR 419 Prev <— Closed —> Next DR 421, or summary at top


    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
    

    Oct 2012 meeting

    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.


    DR 419 Prev <— Closed —> Next DR 421, or summary at top



    DR 421

    DR 420 Prev <— Closed —> Next DR 422, or summary at top


    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.


    Oct 2012 meeting

    Committee Discussion

    Apr 2013 meeting

    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.


    DR 420 Prev <— Closed —> Next DR 422, or summary at top



    DR 422

    DR 421 Prev <— Closed —> Next DR 424, or summary at top


    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;


    Oct 2012 meeting

    Committee Discussion

    Apr 2013 meeting

    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.


    DR 421 Prev <— Closed —> Next DR 424, or summary at top



    DR 423

    DR 407 Prev <— Open —> Next DR 427, or summary at top


    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:

    1. the effective type of the resulting rvalue is unqualified
    2. all operators that will accept the rvalue as an operand will act all the same whether the type is qualified or not

    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.

    Discussion

    Importance of observability of qualifiers

    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:

    Lvalue conversion of the controlling expression of the generic selection is not a solution

    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.

    Origin

    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.

    Operations

    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.

    Comparison to C++

    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.

    Proposal 1: Require the implementation to specify its choice

    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.

    Proposal 2: Keep all qualifiers on types of rvalue expressions

    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 accumulate
    
    This strategy wouldn't work for the narrow types, because the are promoted to signed or unsigned.

    Proposal 3: Require the implementation to provide a feature test macro

    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.

    Proposal 4: Drop all qualifiers from the controlling expression of the generic selection

    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.

    Proposal 5: Drop all qualifiers of rvalues

    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.

    Proposal 6: Add a 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.

    The third addedum would be the same as in the previous case:

    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.


    Oct 2012 meeting

    Committee Discussion

    Apr 2013 meeting

    Committee Discussion

    Oct 2013 meeting

    Committee Discussion

    Apr 2014 meeting

    Committee Discussion


    Apr 2015 meeting

    Committee Discussion

    The paper N1863 was provided and its Suggested Technical Suggestion was adopted.

    Proposed Technical Corrigendum

    Change 6.5.4.p5 from

    Preceding an expression by a parenthesized type name converts the value of the expression to the named type. This construction is called a cast104. A cast that specifies no conversion has no effect on the type or value of an expression.

    104) 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 that type.

    to
    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 cast104. A cast that specifies no conversion has no effect on the type or value of an expression.

    104) A cast does not yield an lvalue.

    Change 6.7.6.3 p5 from
    ... then the type specified for ident is “derived-declarator-type-list function returning T”.
    to
    ... then the type specified for ident is “derived-declarator-type-list function returning the unqualified version of T”.

    DR 407 Prev <— Open —> Next DR 427, or summary at top



    DR 424

    DR 422 Prev <— Closed —> Next DR 425, or summary at top


    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

    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 to key set to a value x that is not null, the destructor function *dtor is called with dtor(x) before the thread exits.
    This call to dtor is executed in the context of the same thread; it is sequenced after the return statement or the call to thrd_exit that terminates the thread and before any return from thrd_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 for key issues calls to tss_set, tss_get or tss_delete with the same key the behavior is undefined.
    tss_set can be used to set the value of a thread specific storage for a different key key2 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 the return of the thread function or at the end of thrd_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 by TSS_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 of key will not change the observable behavior of any of the active threads. If tss_delete is called for key and there is a thread that has a non-null value for key that has passed a terminating return statement or call to thrd_exit but not yet finished the processing of all its tss destructors, the behavior is undefined.

    Oct 2012 meeting

    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 of thrd_exit and tss_delete.
    Oct 2013 meeting

    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.


    DR 422 Prev <— Closed —> Next DR 425, or summary at top



    DR 425

    DR 424 Prev <— Closed —> Next DR 426, or summary at top


    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.

    Oct 2012 meeting

    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.


    DR 424 Prev <— Closed —> Next DR 426, or summary at top



    DR 426

    DR 425 Prev <— Closed —> Next DR 428, or summary at top


    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:

    1. The two operands are different NaNs, negate flips the sign of a NaN, and the result of * and / depends upon the sign and value of the NaN.
    2. The result is a NaN from non-NaN operands, negate does not flip the sign of a NaN, while both * and / set the sign of the result as the XOR of the signs of the operands.
    3. All operands are non-NaN, the result is inexact and non-NaN, and a rounding that is not symmetric about zero is in effect.

    Suggested Technical Corrigendum


    Apr 2013 meeting

    Committee Discussion

    Oct 2013 meeting

    Committee Discussion

    Proposed Technical Corrigendum

    In the table in G.5.1 #2, change

    -yv

    to

    (-y)v
    in three places.

    In the table in G.5.1 #3, change

    -x/v

    to

    (-x)/v
    in two places.


    DR 425 Prev <— Closed —> Next DR 428, or summary at top



    DR 427

    DR 423 Prev <— Open —> Next DR 431, or summary at top


    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

    Oct 2013 meeting Apr 2014 meeting

    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 (superceded)

    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.

    Apr 2015 meeting

    Committee Discussion

    The goal of preserving conversions as if by assignment is fulfilled by the definition of initialization found in 6.7.9 Initialization paragraph 11. Another instance of assignment that should be changed was found in 6.9.1 Function Definitions paragraph 11.

    It was noted that implicit conversion is described only in terms of assignment (6.5.16.1). There was broad agreement that committee members and implementors are unconfused by the intent of the standard here despite the inconsistencies. It was also noted that initialization is distinct from assignment and, in the case of non-lock free atomic implications, this requires operational differences and as such that it is worth further consideration. As such, the following should be regarded as a possible direction.

    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.

    In 6.9.1 paragraph 11 change:

    After all parameters have been assigned,

    to

    After all parameters have been initialized,

    DR 423 Prev <— Open —> Next DR 431, or summary at top



    DR 428

    DR 426 Prev <— Closed —> Next DR 429, or summary at top


    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)

    In the "Runtime-constraints" section, K.3.5.3.5p2 first sentence it says:

    "Neither s nor format shall be a null pointer. n shall neither equal
    zero nor be greater than RSIZE_MAX."

    So,
        if (n == 0 || n > RSIZE_MAX)
            /* runtime constraints violation */

    This is clear. However the next paragraph K.3.5.3.5p3, says this about "s":

    "If there is a runtime-constraint violation, then if s is not a null
    pointer and n is greater than zero and less than RSIZE_MAX, then the
    snprintf_s function sets s[0] to the null character."

    So, it takes action when (n < RSIZE_MAX)

            if (s != NULL && n > 0 && n < RSIZE_MAX)
                s[0] = '\0';

    Question here is, what if n equals RSIZE_MAX? Should we still reset
    s[0]?

    If I were to say this looks like a typo, would WG14 agree with me?

    That is the text of K.3.5.3.5p3 should be:

      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.
     
    This issue applies to all the sprintf family of routines in Annex K 

    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.


    Apr 2013 meeting

    Committee Discussion

    Oct 2013 meeting

    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.


    DR 426 Prev <— Closed —> Next DR 429, or summary at top



    DR 429

    DR 428 Prev <— Closed —> Next DR 430, or summary at top


    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:

    "If there is a runtime-constraint violation, s[0] is set to the null
    character, and characters are read and discarded from stdin until a
    new-line character is read, or end-of-file or a read error occurs."

    The runtime-constraint violation here can be caused by a null "s"
    pointer.  Should we discard the next input line even if (s == NULL) ?

    The way it is written, it looks like the answer is yes.  However it is
    not clear to us that that was the intent.  Note also that s[0] cannot be
    set to the null character when s==NULL.

    Suggested Technical Corrigendum


    Apr 2013 meeting

    Committee Discussion

    Oct 2013 meeting

    Committee Discussion

    Proposed Technical Corrigendum

    In Annex K.3.5.4.1, replace paragraph 3 with the following:
    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.


    DR 428 Prev <— Closed —> Next DR 430, or summary at top



    DR 430

    DR 429 Prev <— Closed —> Next DR 432, or summary at top


    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:

      name shall not be a null pointer. maxsize shall neither equal zero nor be greater than
      RSIZE_MAX. If maxsize is not equal to zero, then value shall not be a null pointer.

    Question here is, if maxsize really cannot be 0.  If it cannot be
    zero, why does the 2nd sentence mention the condition that (maxsize != 0)?

    If maxsize can be 0, it would allow the value to be a null pointer
    which allows what is described in 6.6.2.1 of TR24731 (N1173) cleanly:

      The getenv_s function can also be used to get the size needed to
      represent the result. This allows the programmer to first call
      getenv_s to get the size, then allocate a buffer to hold the result,
      and then call getenv_s again to actually obtain the result."

    if maxsize can be zero, then I think we would get the length of string thusly:
        getenv_s(&len, NULL, 0, "HOME");

    However, since maxsize cannot be 0 which also requires value not to be
    a null pointer, we would need to do something like this:
        getenv_s(&len, something, 1, "HOME");

    AFAICT, getnenv_s as specified in C11 exactly matches what was in TR24731 (N1172).
    What is in TR24731 (N1172) does not coincide with what is in the rational
    for TR24731 (N1173).  The wording in TR24731 (N1172) (and by extension
    C11) is awkward and it certainly looks like an update intended to correspond to
    the rational for TR24731 (N1173) was either misapplied or not applied.

    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.


    Apr 2013 meeting

    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.


    DR 429 Prev <— Closed —> Next DR 432, or summary at top



    DR 431

    DR 427 Prev <— Open —> Next DR 437, or summary at top


    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 functions

    7.17.7.4p2 Description

      Atomically, compares the value pointed to by object for equality with
      that in expected, and if true, replaces the value pointed to by object
      with desired, and if false, updates the value in expected with the
      value pointed to by object.

    When object is an atomic struct type and expected is the corresponding
    non-atomic struct type.  What does it mean to compare two struct types
    as equal?

    Where does the C standard define what it means for two objects of struct
    type to be equal?

    7.17.7.4 NOTE 1 gives an example using memcmp on how the test for
    equality might be defined.  But that is non-normative.

    But the padding bytes in a struct have unspecified values (6.2.6.1p6)

    7.24.4.1 The memcmp function, footnote 310 reminds us that the contents
    of padding in a struct is indeterminate.

    Even integers can have padding bits, whose values are unspecified (6.2.6.2p1)

    A similar issue probably occurs for Atomic union types.

    Suggested 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.


    Apr 2013 meeting

    Committee Discussion

    Oct 2013 meeting

    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.

    Apr 2015 meeting

    Committee Discussion

    The paper N1906 was provided and discussed and its Proposed Technical Corrigendum was adopted. This resolution clarifies that

    Proposed Committee Response

    atomic_compare_exchange is now aligned with C++11 as operating on bit representations. Where these representations are unpadded integer or structure values, the operation is well defined. The type bool is padded in many implementations.

    Proposed Technical Corrigendum

    In 7.17.7.4p2 replace

    Atomically, compares the value pointed to by object for equality with that in expected, and if true, replaces the value pointed to by object with desired, and if false, updates the value in expected with the value pointed to by object
    with:
    Atomically, compares the contents of the memory pointed to by object for equality with that in expected, and if true, replaces the contents of the memory pointed to by object with expected, and if false, updates the value in expected with the value pointed to by object.

    DR 427 Prev <— Open —> Next DR 437, or summary at top



    DR 432

    DR 430 Prev <— Closed —> Next DR 433, or summary at top


    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.

    There are many places in C11 that allow for a representable floating-point zero value.

    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


    Apr 2013 meeting

    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 ...


    DR 430 Prev <— Closed —> Next DR 433, or summary at top



    DR 433

    DR 432 Prev <— Closed —> Next DR 434, or summary at top


    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 function
    The strncat_s() has a constraint that neither s1max nor n shall be greater than RSIZE_MAX.
    Both s1max and n are defined as representing a number of char sized characters.

    K.3.9.2.1.2 The wcsncpy_s function
    The same constraint is given for the function the wcsncat_s() function, i.e. that neither s1max nor n shall be greater than RSIZE_MAX.  For wcsncat_s(), s1max and n are defined as representing a number of wchar_t sized characters.  On most implementations the size of a wide characters is many times the size of a char.  On Solaris it is 4 time the size.

    K.3.4 Integer types <stdint.h> is defined as follows
    1 The header <stdint.h> defines a macro.
    2 The macro is

          RSIZE_MAX

    which expands to a value 386) of type size_t. Functions that have parameters of type rsize_t consider it a runtime-constraint violation if the values of those parameters are greater than RSIZE_MAX.

    386) The macro RSIZE_MAX need not expand to a constant expression.

    Recommended practice

    3 Extremely large object sizes are frequently a sign that an object's size was calculated incorrectly. For example, negative numbers appear as very large positive numbers when converted to an unsigned type like size_t. Also, some implementations do not support objects as large as the maximum value that can be represented by type size_t.

    4 For those reasons, it is sometimes beneficial to restrict the range of object sizes to detect programming errors. For implementations targeting machines with large address spaces, it is recommended that RSIZE_MAX be defined as the smaller of the size of the largest object supported or (SIZE_MAX >> 1), even if this limit is smaller than the size of some legitimate, but very large, objects. Implementations targeting machines with small address spaces may wish to define RSIZE_MAX as SIZE_MAX, which means that there is no object size that is considered a runtime-constraint violation.



    The recommended practice implies RSIZE_MAX represents maximum object sizes.

    Footnote 386) implies an implementation can adjust what RSIZE_MAX expands to depending upon the context in which it is being used.  But what I don't understand is how, the user can take advantage of RSIZE_MAX to check the values of n and s1max prior to calling the function wcsncpy_s in order to avoid violating the runtime constraint error.  There is no context in which the implementation can expand RSIZE_MAX to the value they need.

    Example:

      if ((s1max <= RSIZE_MAX) & (n <= RSIZE_MAX))
         error = wcsncpy_s (s1, s1max, s2 n);  // Assume no other runtime constraints
         if (error != 0) {
            // Since RSIZE_MAX is not a constant expression
            // Can this ever occur due to s1max or n being greater than RSIZE_MAX?
         }
      }

    Is a conforming implementation allowed  to return a non-zero value for wcsncpy_s() in the example above?

    N1147 the Rationale for TR24731 explains implementations might wish to adjust the value of RSIZE_MAX dynamically, and gives several scenarios for doing so. None of which seem germane to the questions raised here.



    So what is the purpose of providing the macro RSIZE_MAX?
    If the purpose is to limit all buffer sizes to RSIZE_MAX, it's use in constraints for wide character functions appear to be malformed.

    The definitions of wcsncpy_s() and strncat_s() have constraints that treat their arguments that represent character counts as if those counts represent the size of an object that can be tested against RSIZE_MAX in the same way.  But those character counts represent characters of very different sizes.  And thus very different object sizes.  Maybe the constraint error for wcsncpy_s() arguments smax1 and n should be rewritten as something like:

      Neither (s1max * sizeof(wchar_t)) nor (n * sizeof(wchar_t)) shall be greater than RSIZE_MAX.

    Other functions where max argument represent the number of
    wchar_t or multi-byte characters and may need similar changes
    include:

    mbstowcs_s
    wcstombs_s
    snwprintf_s
    swprintf_s
    swscanf_s
    vsnwprintf_s
    vswprintf_s
    wcscpy_s
    wcsncpy_s
    wmemcpy_s
    wmemmove_s
    wcscat_s
    wcstok_s
    wcrtomb_s
    mbsrtowcs_s
    wcsrtombs_s

    Suggested Technical Corrigendum


    Apr 2013 meeting

    Committee Discussion

    Oct 2013 meeting

    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".


    DR 432 Prev <— Closed —> Next DR 434, or summary at top



    DR 434

    DR 433 Prev <— Closed —> Next DR 435, or summary at top


    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:

    Constraints

    Atomic type names shall not be used if the implementation does not support atomic types (see 6.10.8.3).


    Oct 2013 meeting

    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).


    DR 433 Prev <— Closed —> Next DR 435, or summary at top



    DR 435

    DR 434 Prev <— Closed —> Next DR 436, or summary at top


    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).

    Oct 2013 meeting

    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.


    DR 434 Prev <— Closed —> Next DR 436, or summary at top



    DR 436

    DR 435 Prev <— Closed —> Next DR 438, or summary at top


    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?


    Oct 2013 meeting

    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):


    DR 435 Prev <— Closed —> Next DR 438, or summary at top



    DR 437

    DR 431 Prev <— Open —> Next DR 439, or summary at top


    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.


    Oct 2013 meeting

    Committee Discussion

    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.

    Apr 2015 meeting

    Committee Discussion

    The paper N1895 was provided and discussed. The general sentiment in the committee is that clock_t is underspecified and that this function should be deprecated and replaced in a revision to the standard with something that uses, perhaps, struct timespec. In particular, no implementations are known to implement the -1 return value on overflow.

    The committee reviewed the following words and approved them as the Proposed Technical Corrigendum.

    Proposed Committee Response

    To question 1, such programs are not conforming and, no, the standard should not be altered to accept this behavior.

    To question 2, no, this is not the direction.

    To question 3, the committee does not agree that this invokes undefined behavior. The value returned under such conditions is unspecified.

    Proposed Technical Corrigendum

    In 7.27.2.1p3 change:

    If the processor time used is not available or its value cannot be represented, the function returns the value (clock_t)(-1)319. ...

    319) 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.

    to
    If the processor time used is not available, the function returns the value (clock_t)(-1). If the value cannot be represented, the function returns an unspecified value319. ...

    319) This may be due to overflow of the clock_t type.

    DR 431 Prev <— Open —> Next DR 439, or summary at top



    DR 438

    DR 436 Prev <— Closed —> Next DR 440, or summary at top


    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

    Change
    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.
    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.

    Add a footnote to 7.29.3.10 paragraph 5, second sentence:
    Note that a file positioning function may further modify the file position indicator after discarding any pushed-back wide characters.

    DR 436 Prev <— Closed —> Next DR 440, or summary at top



    DR 439

    DR 437 Prev <— Open —> Next DR 441, or summary at top


    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:

    1. A full expression is an expression that is not part of another expression or of a declarator.
    2. Each of the following is a full expression:
      1. an initializer that is not part of a compound literal;
      2. the expression in an expression statement;
      3. the controlling expression of a selection statement (if or switch);
      4. the controlling expression of a while or do statement;
      5. each of the (optional) expressions of a for statement;
      6. the (optional) expression in a return statement.
    3. 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.

    1. 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.

    2. 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.

    3. 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.

    4. 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.

    5. 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.


    Oct 2013 meeting

    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.

    Apr 2015 meeting

    Committee Discussion

    There was no paper submitted on this topic, and the committee has solicited the author for suggested technical corrigenda.

    DR 437 Prev <— Open —> Next DR 441, or summary at top



    DR 440

    DR 438 Prev <— Closed —> Next DR 442, or summary at top


    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:

    Oct 2013 meeting
    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.


    DR 438 Prev <— Closed —> Next DR 442, or summary at top



    DR 441

    DR 439 Prev <— Open —> Next DR 444, or summary at top


    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 2013 meeting The committee adopted a Proposed Committee Response that has been substantially revised.

    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, 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.


    Apr 2015 meeting

    Proposed Committee Response

    The implementation of long double, for example, may significantly differ from IEC floating types and may not support the same choices as would otherwise be possible for FLT_ROUNDS. All known implementations define FLT_ROUNDS as the value 1 (round to nearest). and as such exempting non-IEC long double behavior allows the potential for implementations to provide the full range of possible values for IEC floating types.

    Proposed Technical Corrigendum

    At the end of F.2#1, insert

    The value of FLT_ROUNDS applies to all IEC 60559 types supported by the implementation, but need not apply to non-IEC 60559 types.

    DR 439 Prev <— Open —> Next DR 444, or summary at top



    DR 442

    DR 440 Prev <— Closed —> Next DR 443, or summary at top


    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.

    Oct 2013 meeting

    Committee Discussion

    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.


    DR 440 Prev <— Closed —> Next DR 443, or summary at top



    DR 443

    DR 442 Prev <— Closed —> Next DR 446, or summary at top


    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.

    Oct 2013 meeting

    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.


    DR 442 Prev <— Closed —> Next DR 446, or summary at top



    DR 444

    DR 441 Prev <— Open —> Next DR 452, or summary at top


    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.)


    Oct 2013 meeting

    Committee Discussion

    Apr 2014 meeting

    Committee Discussion


    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.

    Apr 2015 meeting

    Committee Discussion

    The suggested syntax provided by the author has been adopted on a trial basis in at least one implementation. It does not, however, provide for compound literals.

    A simpler syntax change was discussed, to wit

    specifier-qualifier-list:
    type-specifier specifier-qualifier-listopt
    type-qualifier specifier-qualifier-listopt
    alignment-specifier specifier-qualifier-listopt

    where specifier-qualifier-list is used in the grammar in only two productions: struct-declaration (which relates to the primary purpose of this DR), and type-name, which is used only in the definitions of these constructs:

    A constraint could be added to 6.7.7 type-name after paragraph 1 disallowing the use of alignment-specifier in a type-name except in the case of compound literal which was deemed useful by the committee. The following principles were elucidated:

  • _Alignas needs to be applied wherever objects are laid out in memory. On modern architectures page and cache line alignment of data structures is increasingly critical for performance.
  • Alignment is incorporated into the type system for structure (and union) members, but otherwise is not considered part of the type.
  • In 6.7.3p5, there are two references to specifier-qualifier-list, which should also reference declaration specifiers.

    In 6.7.5 paragraphs 2 and 4, there are occurrences of the phrase “alignment attribute” which should instead read “alignment specifier”

    DR 441 Prev <— Open —> Next DR 452, or summary at top



    DR 445

    DR 471 Prev <— Review —> Next DR 448, or summary at top


    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:

    The following principles seem natural for any fix for this issue:


    Oct 2013 meeting

    Committee Discussion

    Apr 2014 meeting

    Committee Discussion

    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:

    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".


    DR 471 Prev <— Review —> Next DR 448, or summary at top



    DR 446

    DR 443 Prev <— Closed —> Next DR 447, or summary at top


    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.1 memcpy, 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

    The memcpy function copies n characters from the object pointed to by s2 into the object pointed to by s1.

    to

    The memcpy function copies n bytes from the object pointed to by s2 into the object pointed to by s1.

    memmove

    Change 7.24.2.2 p 2 from

    The memmove function copies n characters from the object pointed to by s2 into the object pointed to by s1. Copying takes place as if the n characters from the object pointed to by s2 are first copied into a temporary array of n characters that does not overlap the objects pointed to by s1 and s2, and then the n characters from the temporary array are copied into the object pointed to by s1.

    to

    The memmove function copies n bytes from the object pointed to by s2 into the object pointed to by s1. Copying takes place as if the n bytes from the object pointed to by s2 are first copied into a temporary array of n bytes that does not overlap the objects pointed to by s1 and s2, and then the n bytes from the temporary array are copied into the object pointed to by s1.

    memcmp

    Change 7.24.4.1 p 2 from

    The memcmp function compares the first n characters of the object pointed to by s1 to the first n characters of the object pointed to by s2.

    to

    The memcmp function compares the first n bytes of the object pointed to by s1 to the first n bytes of the object pointed to by s2.

    Oct 2013 meeting

    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.


    DR 443 Prev <— Closed —> Next DR 447, or summary at top



    DR 447

    DR 446 Prev <— Closed —> Next DR 449, or summary at top


    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 type

    1 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 complex

    2 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 imaginary

    1 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.


    DR 446 Prev <— Closed —> Next DR 449, or summary at top



    DR 448

    DR 445 Prev <— Review —> Next DR 450, or summary at top


    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.


    Oct 2013 meeting

    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)


    DR 445 Prev <— Review —> Next DR 450, or summary at top



    DR 449

    DR 447 Prev <— Closed —> Next DR 451, or summary at top


    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

    Apr 2014 meeting

    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.


    DR 447 Prev <— Closed —> Next DR 451, or summary at top



    DR 450

    DR 448 Prev <— Review —> Next DR 455, or summary at top


    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.

    Suggested Technical Corrigendum:

    Change paragraph 8 in the Returns section of tmpnam_s to read:

    Oct 2013 meeting

    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:

    DR 448 Prev <— Review —> Next DR 455, or summary at top



    DR 451

    DR 449 Prev <— Closed —> Next DR 454, or summary at top


    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:

    1. Can an uninitialized variable with automatic storage duration (of a type that does not have trap values, whose address has been taken so 6.3.2.1p2 does not apply, and which is not volatile) change its value without direct action of the program?
    2. If the answer to question 1 is "yes", then how far can this kind of "instability" propagate?
    3. If "unstable" values can propagate through function arguments into a called function, can calling a C standard library function exhibit undefined behavior because of this?

    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.

    Motivation and discussion

    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.

    Possible Resolutions

    We see three reasonable sets of answers to these questions:

    Resolution (a)

    1. no
    2. not applicable
    3. not applicable

    Advantage

    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.

    Disadvantage

    Restricts the kind of optimizations compilers are allowed to perform.

    Resolution (b)

    1. yes
    2. any operation performed on indeterminate values will have an indeterminate value as its result
    3. no

    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.

    Advantage

    Gives compilers more freedom to perform optimizations.

    Is Defect Report #260-compliant (i.e., "the committee did not change its mind").

    Disadvantage

    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".

    Resolution (c)

    1. yes
    2. any operation performed on indeterminate values will have an indeterminate value as its result
    3. yes, library functions will exhibit undefined behavior when used on indeterminate values (probably functions like memcpy and maybe fwrite should be immune from this)

    Advantage

    Restricts program behaviors least, giving compilers even more freedom.

    Disadvantage

    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.

    Suggested Technical Corrigendum

    For resolution (a)

    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)."

    For resolution (b)

    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.

    For resolution (c)

    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.


    Oct 2013 meeting

    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


    DR 449 Prev <— Closed —> Next DR 454, or summary at top



    DR 452

    DR 444 Prev <— Open —> Next DR 453, or summary at top


    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)

    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.


    Oct 2013 meeting

    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 example

    
    union u2 {
        int x;
        long y;
        char ca[2];
    };
    
    int func2(void) {
        union u2 o2 = { .ca = "a" };
    
    and question, what is the result of (0,o2).ca == o2.ca?

    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.

    Apr 2015 meeting

    Committee Discussion

    The following words were drafted and approved by the committee as the Proposed Technical Corrigendum.

    Proposed Committee Response

    To the question "Should the expression (O, o2).ca == o2.ca yield zero, non-zero, or should it be implementation defined?" the answer is "implementation defined".

    With the following changes, the effective type of O, o3 is defined.

    Proposed Technical Corrigendum

    In 6.2.4p8, append

    An object with temporary lifetime behaves as if it were declared with the type of its value for the purposes of effective type. Such an object need not have a unique address.

    (add forward reference to 6.5p6 to section)

    DR 444 Prev <— Open —> Next DR 453, or summary at top



    DR 453

    DR 452 Prev <— Open —> Next DR 462, or summary at top


    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.

    Apr 2015 meeting

    Committee Discussion

    The paper N1908 was provided and discussed. "Clears" the flag was vaguely troubling and a new approach was offered:

    In 7.17.8.1p2, change:

    Atomically sets the value pointed to by object to true.

    to:

    Atomically places the atomic flag pointed to by object in the set state and returns the value corresponding to the immediately preceding state.

    In 7.17.8.1p3, change:

    Atomically, the value of the object immediately before the effects.

    to:

    The atomic_flag_test_and_set functions return the value that corresponds to the state of the atomic flag immediately before the effects. The return value true corresponds to the set state and the return value false corresponds to the clear state.

    In 7.17.8.2p2, change:

    Atomically sets the value pointed to by object to false.

    to:

    Atomically places the atomic flag pointed to by object into the clear state.



    DR 452 Prev <— Open —> Next DR 462, or summary at top



    DR 454

    DR 451 Prev <— Closed —> Next DR 457, or summary at top


    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?

    • Implicitly undefined
    • Indeterminate
    • Valid
    • Initialized
    • Something else

    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.

    Apr 2014

    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.


    DR 451 Prev <— Closed —> Next DR 457, or summary at top



    DR 455

    DR 450 Prev <— Review —> Next DR 456, or summary at top


    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?

    • Implicitly undefined
    • Indeterminate
    • Valid
    • Initialized
    • Something else

    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

    • The 7.17.2.1#2 words should not be deleted.
    • 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.

    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.

    DR 450 Prev <— Review —> Next DR 456, or summary at top



    DR 456

    DR 455 Prev <— Review —> Next DR 461, or summary at top


    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:

    1. #define UINT8_C(c) c ## U
    2. #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

    1. Add in suffixes for char and short literals.
    2. Remove the UINT{8,16}_C macros from the standard.

    Apr 2014 meeting

    Committee Discussion

    • The committee believes that DR209 is still appropriate in that "compiler magic" must be used for the implementation of these macros.
    • As such, both proposed resolutions were found inappropriate.


    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.


    DR 455 Prev <— Review —> Next DR 461, or summary at top



    DR 457

    DR 454 Prev <— Closed —> Next DR 458, or summary at top


    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 }))

    Apr 2014 meeting

    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 }))


    DR 454 Prev <— Closed —> Next DR 458, or summary at top



    DR 458

    DR 457 Prev <— Closed —> Next DR 459, or summary at top


    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 ) specifies that the header define a number of macros having the form ATOMIC_XXX_LOCK_FREE that indicate the lock-free property of the corresponding atomic types. No further description of the macros is provided here.

    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 Corrigendum

    In 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


    Apr 2014 meeting

    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); and

    to
    ...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


    DR 457 Prev <— Closed —> Next DR 459, or summary at top



    DR 459

    DR 458 Prev <— Closed —> Next DR 460, or summary at top


    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 Corrigendum

    In 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);
          


    Apr 2014 meeting

    Proposed Technical Corrigendum

    In section 7.17.7.2, paragraph 1, Synopsis, modify the declarations of the atomic_load pair of generic functions from:

              #include <stdatomic.h>
    
              C atomic_load(volatile A *object);
              C atomic_load_explicit(volatile A *object,
                                     memory_order order);
          

    to:

              #include <stdatomic.h>
    
              C atomic_load(const volatile A *object);
              C atomic_load_explicit(const volatile A *object,
                                     memory_order order);
          


    DR 458 Prev <— Closed —> Next DR 460, or summary at top



    DR 460

    DR 459 Prev <— Closed —> Next DR 463, or summary at top


    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 Corrigendum

    The 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 alignment shall be is not a valid alignment supported by the implementation andor the value of size shall 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.

    to:
    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).


    DR 459 Prev <— Closed —> Next DR 463, or summary at top



    DR 461

    DR 456 Prev <— Review —> Next DR 464, or summary at top


    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:

    const 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];
    }

    Missing restriction to access other functions' local objects

    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.

    atomic_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);
    }

    Suggested Technical Corrigendum

    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 handler refers 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

    • The committee agrees that clarifying "refers" would be beneficial.
    • Allowing access to const qualified objects would be a feature and cannot be accomplished by the mechanism of a defect report. Such a feature can be proposed in a separate paper.
    • Access to objects of allocated storage duration should also be addressed.
    • It is suggested that automatic storage duration objects should also be addressed.
    • Further revisions have been solicited from the author.

    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).

    DR 456 Prev <— Review —> Next DR 464, or summary at top



    DR 462

    DR 453 Prev <— Open —> Next DR 469, or summary at top


    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.

    Suggested 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_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).


    Apr 2015 meeting

    Committee Discussion

    The committee noted that atomic_init was not safe to call. It was decided that the best place to say this was in the atomic_init description as a pattern to follow for future possible additions. As such, the following revised Proposed Technical Corrigendum was provided and accepted.

    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 functions in <stdatomic.h> (except where explicitly stated otherwise) 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

    Add a new paragraph after 7.17.2.2 paragraph 3:

    If a signal occurs other than as the result of calling the abort or raise function, the behavior is undefined if the signal handler calls the atomic_init generic function.

    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 functions in <stdatomic.h> (except where explicitly stated otherwise) 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).

    DR 453 Prev <— Open —> Next DR 469, or summary at top



    DR 463

    DR 460 Prev <— Closed —> Next DR 400, or summary at top


    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++14

    It 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

    6.5.7p4 should be modified to read:
    The result of E1 << E2 is E1 left-shifted E2 bit positions; vacated bits are filled with zeros. If E1 has an unsigned type, the value of the result is E1 x 2E2, reduced modulo one more than the maximum value representable in the result type. If E1 has a signed type and nonnegative value, and E1 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.
    C++ addressed this in C++14 with DR1457 with identical wording modifications.

    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.


    DR 460 Prev <— Closed —> Next DR 400, or summary at top



    DR 464

    DR 461 Prev <— Review —> Next DR 465, or summary at top


    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).

    DR 461 Prev <— Review —> Next DR 465, or summary at top



    DR 465

    DR 464 Prev <— Review —> Next DR 466, or summary at top


    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.

    Apr 2015 meeting

    Committee Discussion

    No revisions were deemed necessary. Value 1 remains in 7.17.5p1 for implementations where only the runtime can determine if an operation on a particular type is lock-free due to architectural differences.

    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.



    DR 464 Prev <— Review —> Next DR 466, or summary at top



    DR 466

    DR 465 Prev <— Review —> Next DR 467, or summary at top


    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.

    Suggested Technical Corrigendum

    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.

    DR 465 Prev <— Review —> Next DR 467, or summary at top



    DR 467

    DR 466 Prev <— Review —> Next DR 468, or summary at top


    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),

    • FLT_TRUE_MAX 1E+37
    • DBL_TRUE_MAX 1E+37
    • LDBL_TRUE_MAX 1E+37

    (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 1
    to
    and the least normalized value greater than 1

    DR 466 Prev <— Review —> Next DR 468, or summary at top



    DR 468

    DR 467 Prev <— Review —> Next DR 471, or summary at top


    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.

    Suggested Technical Corrigendum

    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)

    DR 467 Prev <— Review —> Next DR 471, or summary at top



    DR 469

    DR 462 Prev <— Open —> Next DR 470, or summary at top


    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.

    Apr 2015 meeting

    Committee Discussion

    The paper N1907 was presented.

    Issue 1 from that paper has already been addressed in DR414

    Issue 2, that recursive mutex behavior is essentially unspecified, needs addressing, but the words provided are unclear about accounting for additional lock and matching unlocks. It may be necessary to introduce the notion of counting to express the nested pattern succinctly.

    Issue 3, from the original paper, was thought by the committee to be worth addressing, although in which section was not clear to the committee.

    A revised paper was solicited.

    DR 462 Prev <— Open —> Next DR 470, or summary at top



    DR 470

    DR 469 Prev <— Open —> Next DR 472, or summary at top


    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 treat mtx_trylock as if spurious failures were allowed; the memory model is intentionally too weak to support reasoning based on a return value of thrd_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.

    Apr 2015 meeting

    Committee Discussion

    The paper N1922 was presented and adopted with an editorial improvement.

    Proposed Technical Corrigendum

    In 7.26.4.5 replace paragraph 3

    The mtx_trylock function returns thrd_success on success, or thrd_busy if the resource requested is already in use, or thrd_error if the request could not be honored.

    with

    The mtx_trylock function returns thrd_success on success, or thrd_busy if the resource requested is already in use, or thrd_error if the request could not be honored. mtx_trylock may spuriously fail to lock an unused resource, in which case it shall return thrd_busy.

    DR 469 Prev <— Open —> Next DR 472, or summary at top



    DR 471

    DR 468 Prev <— Review —> Next DR 445, or summary at top


    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.
    1. cacosh( 0.0 + I*NaN ) should be NaN + I*pi/2 (not NaN + I*NaN).
    2. 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.

    3. ctanh(+0.0+I*NaN) should be 0.0 + I*NaN (not NaN+I*NaN)
    4. ctanh(+0.0+I*INF) should be 0.0 + I*NaN w/ invalid (not NaN+I*NaN w/ invalid)
    5. 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.

    DR 468 Prev <— Review —> Next DR 445, or summary at top



    DR 472

    DR 470 Prev <— Open —> Next DR 473, or summary at top


    Submitter: Fred J. Tydeman
    Submission Date: 2015-01-07
    Source: WG14
    Reference Document: N1902
    Subject: Introduction to complex arithmetic in 7.3.1p3 wrong due to CMPLX

    Summary

    The introduction to complex arithmetic in 7.3.1p3 is wrong on several counts, all due to CMPLX.

    The text in question is:

    Each synopsis specifies a family of functions consisting of a principal function with one or more double complex parameters and a double complex or double return value; and other functions with the same name but with f and l suffixes which are corresponding functions with float and long double parameters and return values.

    The items that are wrong are:

    • CMPLX is a macro (not a function).
    • CMPLX takes double parameters (not double complex).
    • CMPLX has F and L suffixes (not f and l).

    Suggested Technical Corrigendum


    Apr 2015 meeting

    Committee Discussion

    The following Proposed Technical Corrigendum was presented, discussed, and accepted.

    Proposed Technical Corrigendum

    In 7.3.1#3, change:

    Each synopsis specifies a family of functions

    to

    Each synopsis other than the CMPLX macros specifies a family of functions

    (add forward reference to 7.3.9.3)

    DR 470 Prev <— Open —> Next DR 473, or summary at top



    DR 473

    DR 472 Prev <— Open —> Next DR 474, or summary at top


    Submitter: Fred J. Tydeman
    Submission Date: 2015-01-07
    Source: WG14
    Reference Document: N1903
    Subject: "A range error occurs if x is too large." is misleading

    Summary

    "A range error occurs if x is too large." is misleading (or ambiguous) for expm1 (7.12.6.3p2), erfc (7.12.8.2p2), and lgamma (7.12.8.3p2).

    "too large" could mean either +/-large value (in which case "too small" means +/-near zero) or just +large value (in which case "too small" means -large value).

    7.12.6.3p2: expm1(-DBL_MAX) is -1, which is not a range error.

    7.12.8.2p2: erfc(-DBL_MAX) is 2, which is not a range error.

    7.12.8.3p2: lgamma(-DBL_MAX) is a pole error, which is not a range error.

    Suggested Technical Corrigendum

    Add the word "positive" before x in those three cases so that they are:

    A range error occurs if positive x is too large.

    Apr 2015 meeting

    Committee Discussion

    The Suggested Technical Corrigendum was accepted.

    Proposed Technical Corrigendum

    In 7.12.6.3 The exp2 function p2 change

    A range error occurs if x is too large.237

    to

    A range error occurs if positive x is too large.237

    In 7.12.8.2 The erf function p2 change

    A range error occurs if x is too large.

    to

    A range error occurs if positive x is too large.

    In 7.12.8.3 The erfc function p2 change

    A range error occurs if x is too large.

    to

    A range error occurs if positive x is too large.

    DR 472 Prev <— Open —> Next DR 474, or summary at top



    DR 474

    DR 473 Prev <— Open —> Next DR 475, or summary at top


    Submitter: Blaine Garst
    Submission Date: 2014-11-11
    Source: WG14
    Reference Document: N1909
    Subject: NOTE 1 Clarification for atomic_compare_exchange

    Summary

    In 7.17.7.4 The atomic_compare_exchange generic functions paragraph 3 states

    NOTE 1 For example, the effect of atomic_compare_exchange_strong is

    
        if (memcmp(object, expected, sizeof (*object) == 0)
             memcpy(object, &desired, sizeof (*object));
        else
            memcpy(expected, object, sizeof (*object));
    

    The goal for this note was to show that either object or expected was updated rather than just being a conditional operation on object alone. It is being read by some parties, however, to mean that atomic_compare_and_exchange is intended to do bit comparison instead of value comparison. This is an erroneous reading.

    Consider first non-lock-free atomic types. These obviously require use of the lock, whether inline or in an external table. So the first conclusion is that an implementation must already select different implementations for these generic functions based on whether the type is lock-free or not (ignoring lock bits leads to data races). The basic algorithm is to take the lock on the target object, extract and compare values with expected, and store or update desired as appropriate, and release the lock. The extraction and comparison would likely be done by the compiler through the use of type specific intrinsics that may or may not get inlined by the optimizer.

    Consider second the cases of padded integer types, padded struct or union types, and float types.. All of these types have multiple bit representations for one or more values and will fail erroneously when object and expected differ in representation but not value. An implementation should, as for non-lock-free data types, select an appropriate intrinsic to perform this operation. There are two basic choices for the intrinsic. First, make all these atomic types locking, and use the locking strategy already in place to attain the lock and extract and compare the value bits appropriately. An alternate strategy might be to first "normalize" *object and *expected, and then perform bitwise compare and exchange.

    To support this conclusion, I propose clarifying the note to apply to unpadded lock-free integer types.

    Suggested Technical Corrigendum

    In 7.17.7.4 The atomic_compare_exchange generic functions paragraph 3 replace
    NOTE 1 For example, the effect of atomic_compare_exchange_strong is ...
    with
    NOTE 1 For example, the effect of atomic_compare_exchange_strong is, for unpadded lock-free integer types, atomically ...

    Apr 2015 meeting

    Proposed Committee Response

    This change, especially in light of DR431, was thought to likely add confusion rather than clarify matters, and no change is desired.

    DR 473 Prev <— Open —> Next DR 475, or summary at top



    DR 475

    DR 474 Prev <— Open —> Next DR 406, or summary at top


    Submitter: Blaine Garst
    Submission Date: 2015-04-15
    Source: WG14
    Reference Document: N1927
    Subject: Misleading Atomic library references to atomic types

    Summary

    The 7.17 atomic library section of the standard and the syntax for atomic types arose from different authors. The library section was adopted first and then amended when the syntax proposal was approved during the development of the C11 Standard. The syntax is constructive and applies, with a few exceptions, to all types, including floats and bitfields.

    There are a few unfortunate phrasings remaining in the 7.17 Atomics <stdatomic.h> section, however, that have caused a small degree of confusion and are worth fixing.

    Suggested Technical Corrigendum

    In 7.17.1 Introduction p3 Replace
    and several atomic analogs of integer types.
    with
    and atomic types declared with the _Atomic or _Atomic() construct.

    In 7.17.1 Introduction p5 Replace
    - An A refers to one of the atomic types.
    with
    - An A refers to an atomic type.

    In 7.17.6 Atomic Integer Types paragraph 2 replace
    The semantics of the operations on these types are defined in 7.17.7
    with
    The semantics of the operations on atomic types are defined in 7.17.7


    Apr 2015 meeting

    Committee Discussion

    The first suggested change is incorrect since it is deliberately speaking of the types declared in <std atomic.h>.

    After discussion, the direction is

    In 7.17.1 Introduction p5 replace

    - An A refers to one of the atomic types.
    with
    - An A refers to an atomic type.

    Delete 7.17.6 paragraph 2.

    DR 474 Prev <— Open —> Next DR 406, or summary at top