N2693 = 08-0203
Jens Maurer <Jens.Maurer@gmx.net>
2008-06-28

Requirements on programs and backwards compatibility

Introduction

During the last WG21 meetings, a number of features have been moved to the working draft that silently impose undefined behavior onto a significant number of existing programs. This paper proposes that freedom be given to implementers to provide users with the option to disable one or more of these features globally, while remaining a conforming implementation (1.4 intro.compliance) even with those features turned off.

Motivation

The next revision of C++, called C++0x, is announced to be a major revision with a lot of new features. It is well-understood that some of these features will turn existing programs that are well-formed and well-defined under the rules of C++03 into ill-formed ones under the rules of C++0x. For example, any program that uses one of the new C++0x keywords such as "thread_local" or "alignof" is in that category. An upcoming core issue that removes the long-deprecated conversion from string literals to non-const char * puts some more programs into that category. Such changes, while in some cases burdensome when porting programs to C++0x, are diagnosed and thus do not lead to surprising runtime failures.

The situation is much different with subtle changes to the C++ object and sequencing models, because these changes are often not diagnosable and thus may lead to silent changes in runtime behavior compared to C++03. Visibility of such changes to user programs may depend on the presence or absence of sophisticated compiler optimizations and thus are an impediment to portable programs, the primary reason for having a standard for C++ in the first place. The C++ standards committee should therefore avoid such silent changes.

Threading Support

The introduction of multi-threading caused a large-scale update of the C++ sequencing and object models that, in general, tightened the rules such that existing (by definition, single-threaded) applications are unaffected.

However, the changes proposed in N2660 "Dynamic Initialization and Destruction with Concurrency" would have had the effect of permitting parallelization of initialization in different translation units even for existing single-threaded programs, had there not a condition "if a program starts a thread" been introduced at the last minute.

While at first sight sufficient, this condition is of little help in the real world. If an old, single-threaded program uses some library and that library is upgraded to a C++0x-aware version, and the upgraded library then starts a thread (for whatever reason) in a dynamic initialization of one of its variables with static storage duration, the dynamic initialization of the unsuspecting application's global variables can now be parallelized, if in different translation units. It is obvious that parallel execution of application code that wasn't prepared accordingly is unlikely to work correctly. In this case, some might argue that it is the library's fault to have introduced such atrocities to the program's environment, but it is nonetheless a silent change in behavior that would have been much easier to debug had it been diagnosed.

Section 1.10p1 [intro.multithread] says

... Under a freestanding implementation, it is implementation-defined whether a program can have more than one thread of execution.

However, there is no corresponding provision in clause 30 [threads] that specifies which library interfaces are present in a freestanding implementation that lacks thread support. Further, even after the application of N2678 "Error Handling Specification for Chapter 30 (Threads)", it is not clear which (if any) error is signalled if a thread is created in a freestanding implementation that lacks thread support.

This paper therefore proposes to introduce a feature-test preprocessor macro and adjust clause 30 for the absence of threads.

Garbage Collection Support

The situation with garbage collection is much more blunt: With C++0x, a program that uses a non-safely-derived pointer has undefined behavior. Embedded programs that employ extreme measures for saving memory such as using the XOR trick for doubly-linked lists will have undefined behavior once compiled with a C++0x compiler.

The library function get_pointer_safety() is underspecified at best and might even be considered contradictory with the core language wording. In particular,

Requiring that programs include two implementations of their data structures in their binary (one using safely-derived pointers, the other one saving space by employing non-safely-derived pointers), because garbage collection restrictions that are in force can only be detected at runtime seems overly burdensome for resource-limited environments; flash memory for storing program images is expensive.

This paper therefore proposes to make garbage collection support conditionally-supported at least in a freestanding environment, with an associated feature-test preprocessor macro.

Proposed Wording Changes

Change in 3.6.2 basic.start.init paragraph 1 (see N2660: Dynamic Initialization and Destruction with Concurrency):
If a program can have more than one thread of execution (1.10 intro.multithread, 18.4 support.start.term) and if it starts a thread (30.2 [thread.threads]), the subsequent initialization of an object is unsequenced with respect to the initialization of an object defined in a different translation unit.
Change in 3.7.3.3 basic.stc.dynamic.safety paragraph 4 (see N2670: Minimal Support for Garbage Collection and Reachability-Based Leak Detection (revised)):
An implementation may have relaxed pointer safety, in which case the validity of a pointer value does not depend on whether it is a safely-derived pointer value or not. Alternatively, an implementation may have strict pointer safety, in which case if If a pointer value that is not a safely-derived pointer value is dereferenced or deallocated, and the referenced complete object is of dynamic storage duration and has not previously been declared reachable ([util.declare_reachable]), the behavior is undefined. [ Note: This is true even if the unsafely-derived pointer value might compare equal to some safely-derived pointer value. -- end note ] It is implementation-defined whether an implementation has relaxed or strict pointer safety.
Add a new paragraph 4 at the end of 17.4.1.3 compliance:
The header <thread> shall be present if and only if a program can have more than one thread of execution (1.10 intro.multithread).
Add two new paragraphs 13+14 at the end of 18.4 support.start.term:

__STDCPP_THREADS
The macro __STDCPP_THREADS shall be defined if and only if a program can have more than one thread of execution (1.10 intro.multithread). If the macro is defined, it shall have the same value as the predefined macro __cplusplus (16.8 cpp.predefined). [ Footnote: With this facility, an application or library can make the implementation issue a diagnostic if it cannot operate in a multi-threaded environment, see 3.6.2 basic.start.init. ]

__STDCPP_STRICT_POINTER_SAFETY
The macro __STDCPP_STRICT_POINTER_SAFETY shall be defined if and only if the implementation has strict pointer safety (3.7.3.3). If the macro is defined, it shall have the same value as the predefined macro __cplusplus (16.8 cpp.predefined). [ Footnote: With this facility, a program can make the implementation issue a diagnostic if the program requires relaxed pointer safety. ]

In 20.6.8, replace the description of get_pointer_safety (see N2670: Minimal Support for Garbage Collection and Reachability-Based Leak Detection (revised)) by
pointer_safety get_pointer_safety()

Returns: Returns pointer_safety::strict if the implementation has strict pointer safety (3.7.3.3). It is implementation-defined whether get_pointer_safety returns pointer_safety::relaxed or pointer_safety::preferred if the implementation has relaxed pointer safety (3.7.3.3) [ Footnote: pointer_safety::preferred might be returned to indicate to the program that a leak detector is running so that the program can avoid spurious leak reports. -- end footnote ].

Throws: nothing