This is the maximum length of the printed representation for a NaN. In our case, it's for something like -NAN(123).
This is a new feature for C23, but since it was a reserved identifier under earlier standards, we just define it unconditionally.
As with printf, the format checker expects 'b' to have its traditional ORCA/C behavior in pre-C23 modes, or the C23 standard behavior in C23 modes.
Also as with printf, it can detect when 'b' is being used in the traditional way and automatically change it to 'p'. Specifically, this is done when 'b' is used with no length modifier and the corresponding argument is a pointer to a character type. This behavior is controlled by the same #pragma extensions bit as for printf, and it also only works when the scanf-family function is called directly and has a string literal for the format string.
This uses the printf format-checking mechanism to parse the format string and associate conversions in it with arguments. If the argument corresponding to 'b' is a pointer (and no length modifier is used), the 'b' is converted to 'P'. The effect is that most code that used 'b' in the old way will keep working, but code using 'b' with integers will get the new behavior (in C23 mode). Note that this only works if the format string is given by a string literal, and the printf-family function is called directly (not via a function pointer).
This is controlled bit a new #pragma extensions bit (bit 2), which is enabled by default when not in strict mode.
When compiling in C23 mode, ORCA/C now generates references to the label ~C23ORLATER, which is defined in the library. The presence of this label in the linked executable can be used by library functions to determine whether they should operate in C23 mode.
This applies to code like the following, which is allowed by C89 but not C99+:
int f(a) { /* code using a, implicitly declared as int */ }
Such code was inadvertently broken in c23compat mode due to changes to handle C23 rules, but it should be allowed in that mode for compatibility with old code. It is still disallowed by strict mode, or by appropriate lint checks.
These are similar to the ones from the last commit, in that there are earlier checks for the pointer being nil, but the nil dereferences can still occur because short-circuit evaluation is not used. These ones have somewhat more complicated control flow, and also the added aspect that a value read through the nil pointer is itself treated as a pointer and dereferenced. This still doesn't cause problems most of the time, but dereferencing that garbage value may possibly hit a soft switch or IO location. Apart from fixing that potential issue, there should be no functional changes.
These were in code like "if (ptr <> nil) and (ptr^.field = XYZ) then ..." or similar. ORCA/Pascal does not use short-circuit evaluation for boolean operators, so the code that would dereference the pointer would get evaluated even if it was nil. In the situations here, this was basically harmless, since the IIGS does not have memory protection and the value read from the nil pointer would not affect the program's behavior. However, it is still cleaner to avoid the nil pointer dereferences.
(Some similar code with more complex control flow is still present.)
This implements the enhancements to enum types in C23. There are three major elements to these enhancements, which are all implemented here:
-Enumeration constants can have values outside the range of int; enum types using the traditional syntax are automatically assigned an underlying type that is able to hold their constant values (WG14 N3029)
-The underlying type for an enumeration can be explicitly specified using a new syntax (WG14 N3030)
-Enumeration types and their constants can be declared multiple times within the same scope (parts of WG14 N3037)
This gives an error when an expression cast to void is used somewhere that an integer constant expression is required, e.g.:
int main(int argc, char *argv[]) {
switch (argc) {
case (void)0: 1;
}
}
One bug is that the scope of the enum constant only begins at the end of the enumerator, so a definition of the same name from an outer scope should be visible and usable within the expression giving the enum constant value. The other bug is that spurious errors would be reported if the enum constant matched the name of a typedef from an outer scope.
In ORCA/C, types with kind = enumType are only used for enum type tags. They do not appear in other places, because an enum type specifier is treated as yielding the type int. Thus, there can never be an expression or variable with an enum type. This has been the case in ORCA/C at least back to 2.1.0 (and likely farther). This is technically not quite standard-conformant, but it makes very little difference in practice, because ORCA/C currently makes all enum types compatible with int.
There was code in some places to handle enum-typed expressions, variables, or function return types, but this is dead code. Most of this code looks reasonable as far as I can tell, but it has not actually been used for a long time (if ever), and it is likely incomplete relative to what would actually be needed to support enum-typed expressions and variables.
This patch comments out that dead code and replaces it with calls to produce an error if those code paths are ever triggered, which effectively act as assertions that they are not used. If ORCA/C was ever changed to use enum types more widely, the code for handling them could be uncommented at that time, but it would need to audited and updated as appropriate.
Previously, the code would remove any number of "pointer to" and "array of" elements in the type, and accept it if this ultimately yielded a function type. Thus, the expressions denoting the function to be called could be, e.g., a pointer to a function pointer or an array of function pointers. The code generated in these cases would not work properly, but no error was reported.
Now only function pointers or expressions of function type are accepted, as they should be.
This affects code like function_ptr1 = *function_ptr2. This should be allowed (if the function types are compatible), since the function type is converted to a function pointer type in this context.
This affects equality comparisons where one side has function type (e.g. *function_pointer) and the other side has function pointer type (pointing to a compatible function type) or is a null pointer constant. These should be permitted, because the function type is converted to a function pointer type in this context. ORCA/C could report a spurious error for such comparisons, because it was not always performing those conversions.
(ORCA/C would treat function names as having function pointer type for these purposes, since for those the type conversion is performed elsewhere.)
Here is an example showing several expressions that gave spurious errors:
void f(void) {}
void (*fp)(void);
int main(void) {
*fp == f;
*fp == 0;
*fp == (void *)0;
}
An empty initializer {} is now allowed for numeric or pointer types, and is equivalent to {0}. ORCA/C already allowed empty initializers for structs/unions/arrays.
This means that if the parameter giving the array to search is a pointer to a const-qualified type, the return type will be modified so that its pointed-to type is also const-qualified. This functionality is implemented using macros, so the underlying functions still have the same types as before. In addition to the functions specified as generic in C23, this change is also applied to the non-standard strrpbrk function, since it is analogous to strpbrk and has the same issue of potentially discarding const qualifiers.
Note that under ORCA/C's default loose type checking rules, assignments that discard qualifiers are permitted, so these changes have a somewhat limited safety benefit (but also should very rarely require code changes). If using strict type checking, the type rules for assignments will be enforced as per the standard, so assignments that discard qualifiers will produce an error.
This affects cases where the argument expression to assert contains an un-parenthesized comma, which can occur when using compound literals.
When NDEBUG is not defined, this still requires the argument to be something that would count as one argument to a function (i.e. it cannot use an un-parenthesized comma operator). We do this by including an unevaluated call to a fake function using that argument as part of the expansion of the assert macro. (This behavior is specified by a note in the standard, and it's not entirely clear to me that it follows from the normative text, but I've implemented it that way since that's apparently the intent.)
This permits code like the following, which the standards seem to allow:
int x;
_Bool b = &x;
(The bool variable is always initialized to one in cases like this. If would be undefined behavior if the address evaluated to 0, so we can assume it doesn't.)
This check is currently only enabled under strict type checking, since ORCA/C traditionally did not require it but the C11 and later standards do. (The left operand has always been required to point to a complete type, and the two types have been required to be compatible, but the traditional ORCA/C rules could allow the right operand to point to an incomplete array type.)