Previously, there were a couple problems:
*If the parameter that was passed an empty argument appeared directly after the ##, the ## would permanently be removed from the macro record, affecting subsequent uses of the macro even if the argument was not empty.
*If the parameter that was passed an empty argument appeared between two ## operators, both would effectively be skipped, so the tokens to the left of the first ## and to the right of the second would not be combined.
This example illustrates both issues (not expected to compile; just check preprocessor output):
#pragma expand 1
#define x(a,b,c) a##b##c
x(1, ,3)
x(a,b,c)
Previously, it was not necessarily set correctly for the newly-generated token. This would result in incorrect behavior if that token was an operand to another ## operator, as in the following example:
#define x(a,b,c) a##b##c
x(1,2,3)
There was code that would attempt to use the cType field of the type record, but this is only valid for scalar types, not pointer types. In the case of a pointer type, the upper two bytes of the pointer would be interpreted as a cType value, and if they happened to have one of the values being tested for, incorrect intermediate code would be generated. The lower two bytes of the pointer would be used as a baseType value; this would most likely result in "compiler error" messages from the code generator, but might cause incorrect code generation with no errors if that value happened to correspond to a real baseType.
Code like the following might cause this error, although it only occurs if pointers have certain values and therefore depends on the memory layout at compile time:
void f(const int **p) {
(*p)++;
}
This bug was introduced in commit f2a66a524a.
Division by zero produces undefined behavior if it is evaluated, but in general we cannot tell whether a given expression will actually be evaluated at run time, so we should not report this as a compile-time error.
We still report an error for division by zero in constant expressions that need to be evaluated at compile time. We also still produce a lint message about division by zero if the appropriate flag is enabled.
if a #define is within a function, it could use the local memory pool for string allocation (via Malloc in NextToken, line 5785) which can lead to a dangling memory reference when the macro is expanded.
void function(void) {
#define TEXT "abc"
static struct {
char text[sizeof(TEXT)];
} template = { TEXT };
}
This makes their headers and their specifications in the manual consistent with their actual behavior. The const qualifiers in the headers may prevent errors when using strict type checking.
The second parameter of #pragma float is now optional, and if it missing or invalid then the FPE slot is auto-detected by the start-up code. This is done by calling the new ~InitFloat function in the FPE version of SysFloat.
This allows valid FPE-using programs to be compiled using only #pragma float, with no changes needed to the code itself.
The slot-setting code is only generated if the slot is 1..7, and even then it can be overridden by calling setfpeslot(), so this should not cause compatibility problems for existing code.
It now covers pretty much all the new features, as well as addressing the errata from the release notes and some other miscellaneous issues. The early chapters still need to be updated to refer to a hard disk installation, rather than being based on running it from floppies (which is no longer supported). I'm sure more proofreading and editing would also be beneficial.
This is the ORCA/C 2.0 manual from Opus ][, re-saved in the modern Microsoft Word format and adjusted to fix some formatting issues. In particular, the embedded images needed to be converted to formats that current versions of Word support. The result is very close to the original version, although the pagination winds up slightly different in some places.
When the lint check for undefined variables was enabled, a "lint: unused variable: @struct" would be produced for any function returning a struct or union, due to the special static variable that is created to hold the return value. That spurious lint message is now suppressed.
This prohibits initializers in "extern" declarations within a function, and in the parameter declarations of non-prototyped functions.
Here are examples illustrating these cases:
int main(void) {
extern int i = 50;
}
void f(i)
int i = 60;
{
}