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.
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.
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.
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
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.
If a program 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 126.96.36.199 basic.stc.dynamic.safety paragraph 4 (see N2670: Minimal Support for Garbage Collection and Reachability-Based Leak Detection (revised)):
Add a new paragraph 4 at the end of 188.8.131.52 compliance:
Ifa 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 ]
The headerAdd two new paragraphs 13+14 at the end of 18.4 support.start.term:
<thread>shall be present if and only if a program can have more than one thread of execution (1.10 intro.multithread).
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
__STDCPP_THREADSshall 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_SAFETYshall be defined if and only if the implementation has strict pointer safety (184.108.40.206). 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. ]
pointer_safety::strictif the implementation has strict pointer safety (220.127.116.11). It is implementation-defined whether
pointer_safety::preferredif the implementation has relaxed pointer safety (18.104.22.168) [ Footnote:
pointer_safety::preferredmight be returned to indicate to the program that a leak detector is running so that the program can avoid spurious leak reports. -- end footnote ].