When the C standard was written, rules stating that certain operations invoked Undefined Behavior weren't intended to discourage implementations from defining and documenting useful behaviors for them, but instead said that implementations could implement any behavior (useful or otherwise) they considered most practical. Although the Standard would allow implementations to do anything whatsoever, many implementations have historically been able to offer some guarantees about their behaviors, and in many cases code that can rely upon such guarantees can be written much more cleanly and efficiently than code which has to confine itself to behaviors specified by the Standard.
In recognition of this, many compilers offer options to make them honor behavioral guarantees which the Standard does not require, but which some code relies upon. Unfortunately, there is at present no standard way of specifying such options, nor is there any way to ensure that failure to specify required options will not result in a program which compiles cleanly but whose behavior doesn't match the code.
Further, there are a number of operations to which the standard ascribes "Implementation-Defined" behavior, and where the vast majority of implementations behave identically, but where nothing would forbid an implementation from doing something completely contrary to that common practice [but allowable by the Standard], and no means exists by which a program could refuse compilation on such implementations.
I would propose, therefore, that most uses of the terms "Undefined Behavior" and "Implementation-Defined Behavior" be changed to "Testably-Constrained Behavior" and "Testably-Specified Behaviors", as defined below:
An action invokes "Testably-Constrained Behavior" if implementations are required to report, as defined below, whether or not they make various guarantees with regard to the consequences, and are required to honor any guarantees they makes, but are not required to report that they guarantee anything.
An action invokes "Testably-Specified Behavior" if implementations
are required to provide and report at least some guarantees with regard to the consequences, and honor any guarantees they make. The specific minimum guarantees are described separately for each such behavior.
The goal in either case is that a standardized means be defined by which programs could assert their requirements and refuse compilation on implementations that could not satisfy them. For every form of Testably-Constrained Behavior, one of the allowable options would be "Undefined Behavior" [compiler makes no guarantees]; another option in many cases where most platforms would have one clear and obvious "literal" meaning for an operation, even though the effects might often be unpredictable and outside the jurisdiction of the standard, would be "Literal operation on platform". As an example of the latter, if a compiler specified that division by zero would invoke "literal operation on platform" [note that only compilers for platforms which have a division instruction could specify that], the code generated for "int x=5/0;", if executed, would be required to attempt to divide five by zero using the divide instruction, but would be under no obligation to configure trap vectors to ensure meaningful behavior.
An additional term, useful for describing the behavior of various overflow and integer-type coercions is "Partially-Indeterminate Value". A partially-indeterminate value of an n-bit type may behave as any integer--not necessarily a representable one--which is congruent to some particular value of that type, mod 2^n. If a system with a 16-bit "int", for example, were to specify that overflow yields a Partially-Indeterminate Value, then 32767+1 could arbitrarily behave as 32768, or -32768, or 98304, 163840, or any value which is congruent to -32768 mod 65536. If the system further specifies that storing an out-of-range value into an "int" variable will store a Partially-Indeterminate Value, then each attempt to read the integer could potentially yield a different result, provided that all such results are congruent mod 65536. Note that such a rule would allow a compiler to legitimately assume that it could regard the value of "j" after "j=i+1;" as being larger than "i" [since "j" will either be a defined value larger than "i", or a Partially-Indeterminate Value which may arbitrarily compare larger or smaller than "i"], but it would not allow the compiler to assume that the statement could not be reached with "i" holding 32767.
I would propose, as a baseline implementation, a header stdtcsb.h containing macro definitions that could be used by code to test implementation support for required features and behaviors. Almost any existing compiler which includes support for specific behaviors meeting a program's needs could be upgraded to support this new standard merely by supplying a suitable stdtcsb.h file and, for compilers that allow command-line control over options that are not testable in code, having makefiles that set such options also predefine vendor-specified macros to indicate that they have done so.
A baseline means by which the Standard could allow code to make use of actions whose behavior is not presently not mandated by the Standard, but whose behavior in the intended implementation would meet requirements, would be to define for each form of action that could invoke Undefined Behavior or Implementation-Defined Behavior an identifier which would behave as a compile-time constant bitmask identifying all of the possible consequences that could occur as a result of the action, along with macro or directive __STDC_REQUIRE_TCSB(x, y) which would at minimum refuse compilation if the possible behaviors indicated by "x" included some not allowed by "y". Beyond the fact that the macro/directive would be more concise than using `#if` with `#error` directives, it would also allow compilers to adapt their behavior to suit programs' requirements. A desirable feature of this approach is that almost any existing compiler whose natural behavior would satisfy a program's needs could be made compatible with code demanding such features by adding a suitable stdtcsb.h file and having any make files which specify options controlling compiler behavior also predefine macros to notify the stdtcsb.h file of them.
A lot of code, including high-performance audiovisual CODECs, is subject to two requirements:
1. When given valid input, produce valid output.
2. When given invalid input, do not launch nuclear missiles.
TEST DIV
No comments:
Post a Comment