Compare commits

...

13 Commits

Author SHA1 Message Date
Stephen Heumann 75a928e273 Change division-by-zero tests to cases requiring a constant expression. 2024-02-27 13:06:45 -06:00
Stephen Heumann a545685ab4 Use more correct logic for expanding macros in macro parameters.
Specifically, this affects the case where a macro argument ends with the name of a function-like macro that takes 0 parameters. When that argument is initially expanded, the macro should not be expanded, even if there are parentheses within the macro that it is being passed to or the subsequent program code. This is the case because the C standards specify that "The argument’s preprocessing tokens are completely macro replaced before being substituted as if they formed the rest of the preprocessing file with no other preprocessing tokens being available." (The macro may still be expanded at a later stage, but that depends on other rules that determine whether the expansion is suppressed.) The logic for this was already present for the case of macros taking one or more argument; this extends it to apply to function-like macros taking zero arguments as well.

I'm not sure that this makes any practical difference while cycles of mutually-referential macros still aren't handled correctly (issue #48), but if that were fixed then there would be some cases that depend on this behavior.
2024-02-26 22:31:46 -06:00
Stephen Heumann ce94f4e2b6 Do not pass negative sizes to strncat or strncmp in tests.
These parameters are of type size_t, which is unsigned.
2024-02-24 18:57:50 -06:00
Stephen Heumann 84fdb5c975 Fix handling of empty macro arguments as ## operands.
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)
2024-02-22 21:55:46 -06:00
Stephen Heumann d1847d40be Set numString properly for numeric tokens generated by ##.
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)
2024-02-22 21:42:05 -06:00
Stephen Heumann c671bb71a5 Document recent library bug fixes. 2024-02-22 21:20:33 -06:00
Stephen Heumann a646a03b5e Avoid possible errors when using postfix ++/-- on pointer expressions.
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.
2024-02-09 20:45:14 -06:00
Stephen Heumann 7ca30d7784 Do not give a compile error for division of any integer constant by 0.
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.
2024-02-02 20:03:34 -06:00
Stephen Heumann c9dc566c10 Update release notes. 2024-02-02 18:31:48 -06:00
Stephen Heumann 2ca4aba5c4 Correct a misspelled error code in <gsos.h>. 2024-01-18 17:55:41 -06:00
ksherlock d7cc9b5909
update gsbug.h prototypes and errors based on gsbug and niftylist (#86)
* update gsbug.h prototypes and errors based on gsbug and niftylist

* updates

* remove gsbug 1.7 development error codes
2024-01-18 17:51:22 -06:00
Kelvin Sherlock 586229e6eb #define should always use the global pool....
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 };

}
2024-01-15 22:05:32 -06:00
Stephen Heumann 0aee669746 Update version number for ORCA/C 2.2.1 development. 2024-01-15 21:47:00 -06:00
12 changed files with 103 additions and 36 deletions

8
CC.rez
View File

@ -4,12 +4,12 @@ resource rVersion(1) {
{
2, /* Major revision */
2, /* Minor revision */
0, /* Bug version */
release, /* Release stage */
0, /* Non-final release # */
1, /* Bug version */
development, /* Release stage */
1, /* Non-final release # */
},
verUS, /* Region code */
"ORCA/C", /* Short version number */
"Copyright 1997, Byte Works, Inc.\n" /* Long version number */
"Updated 2023"
"Updated 2024"
};

View File

@ -112,7 +112,7 @@ const
flag_t = $00001000; {treat all errors as terminal?}
flag_w = $00000200; {wait when an error is found?}
versionStr = '2.2.0'; {compiler version}
versionStr = '2.2.1 dev'; {compiler version}
type
{Misc.}

View File

@ -1414,7 +1414,11 @@ var
op1 := op1 * op2;
slashch : begin {/}
if op2 = 0 then begin
Error(109);
if not (kind in [normalExpression,
autoInitializerExpression]) then
Error(109)
else if ((lint & lintOverflow) <> 0) then
Error(129);
op2 := 1;
end; {if}
if unsigned then
@ -1424,7 +1428,11 @@ var
end;
percentch : begin {%}
if op2 = 0 then begin
Error(109);
if not (kind in [normalExpression,
autoInitializerExpression]) then
Error(109)
else if ((lint & lintOverflow) <> 0) then
Error(129);
op2 := 1;
end; {if}
if unsigned then
@ -1570,7 +1578,11 @@ var
asteriskch : umul64(llop1, llop2); {*}
slashch : begin {/}
if (llop2.lo = 0) and (llop2.hi = 0) then begin
Error(109);
if not (kind in [normalExpression,
autoInitializerExpression]) then
Error(109)
else if ((lint & lintOverflow) <> 0) then
Error(129);
llop2 := longlong1;
end; {if}
if unsigned then
@ -1580,7 +1592,11 @@ var
end;
percentch : begin {%}
if (llop2.lo = 0) and (llop2.hi = 0) then begin
Error(109);
if not (kind in [normalExpression,
autoInitializerExpression]) then
Error(109)
else if ((lint & lintOverflow) <> 0) then
Error(129);
llop2 := longlong1;
end; {if}
if unsigned then
@ -3298,8 +3314,10 @@ var
else
Gen2t(pc_ind, ord(tqVolatile in expressionType^.qualifiers), 0, tp);
if pc_l in [pc_lli,pc_lld] then
if expressionType^.cType in [ctBool,ctFloat,ctDouble,ctLongDouble,
ctComp] then begin
if (expressionType^.kind = scalarType) and
(expressionType^.cType in
[ctBool,ctFloat,ctDouble,ctLongDouble,ctComp])
then begin
t1 := GetTemp(ord(expressionType^.size));
Gen2t(pc_cop, t1, 0, expressionType^.baseType);
end; {if}
@ -3310,8 +3328,10 @@ var
Gen0t(pc_cpi, tp);
Gen0t(pc_bno, tp);
if pc_l in [pc_lli,pc_lld] then {correct the value for postfix ops}
if expressionType^.cType in [ctBool,ctFloat,ctDouble,ctLongDouble,
ctComp] then begin
if (expressionType^.kind = scalarType) and
(expressionType^.cType in
[ctBool,ctFloat,ctDouble,ctLongDouble,ctComp])
then begin
Gen0t(pc_pop, expressionType^.baseType);
Gen2t(pc_lod, t1, 0, expressionType^.baseType);
Gen0t(pc_bno, expressionType^.baseType);

View File

@ -18,7 +18,7 @@ uses CCommon, MM, Scanner, Symbol, CGI;
{$segment 'HEADER'}
const
symFileVersion = 41; {version number of .sym file format}
symFileVersion = 42; {version number of .sym file format}
var
inhibitHeader: boolean; {should .sym includes be blocked?}

View File

@ -14,13 +14,17 @@
#ifndef __GSBUG__
#define __GSBUG__
/* Error Codes */
#define debugUnImpErr 0xFF01
#define debugBadSelErr 0xFF02
#define dgiProgramCounter 0 /* for DebugGetInfo */
extern pascal Word DebugVersion() inline(0x04FF,dispatcher);
extern pascal Word DebugStatus() inline(0x06FF,dispatcher);
extern pascal void DebugStr() inline(0x09FF,dispatcher);
extern pascal void SetMileStone() inline(0x0AFF,dispatcher);
extern pascal void DebugSetHook() inline(0x0BFF,dispatcher);
extern pascal LongWord DebugGetInfo() inline(0x0CFF,dispatcher);
extern pascal Word DebugVersion(void) inline(0x04FF,dispatcher);
extern pascal Word DebugStatus(void) inline(0x06FF,dispatcher);
extern pascal void DebugStr(Pointer) inline(0x09FF,dispatcher);
extern pascal void SetMileStone(Pointer) inline(0x0AFF,dispatcher);
extern pascal void DebugSetHook(VoidProcPtr) inline(0x0BFF,dispatcher);
extern pascal LongWord DebugGetInfo(Word) inline(0x0CFF,dispatcher);
#endif

View File

@ -126,7 +126,7 @@
#define invalidFSTop 0x0065 /* invalid FST operation */
#define fstCaution 0x0066 /* FST handled call, but result is weird */
#define devNameErr 0x0067 /* device exists with same name as replacement name */
#define defListFull 0x0068 /* device list is full */
#define devListFull 0x0068 /* device list is full */
#define supListFull 0x0069 /* supervisor list is full */
#define fstError 0x006a /* generic FST error */
#define resExistsErr 0x0070 /* cannot expand file, resource already exists */

View File

@ -1311,6 +1311,7 @@ var
class1,class2: tokenClass; {token classes}
i: integer; {loop variable}
kind1,kind2: tokenEnum; {token kinds}
lsaveNumber: boolean; {local copy of saveNumber}
lt: tokenType; {local copy of token}
str1,str2: stringPtr; {identifier strings}
@ -1379,7 +1380,10 @@ else if class1 in numericConstants then begin
end; {else}
workString := concat(tk1.numString^, str2^);
lt := token;
lsaveNumber := saveNumber;
saveNumber := true;
DoNumber(true);
saveNumber := lsaveNumber;
tk1 := token;
token := lt;
goto 1;
@ -1878,6 +1882,8 @@ procedure Expand (macro: macroRecordPtr);
{ Globals: }
{ macroList - scanner putback buffer }
label 1;
type
parameterPtr = ^parameterRecord;
parameterRecord = record {parameter list element}
@ -2156,8 +2162,10 @@ else begin
tcPtr := pptr^.tokens;
if tcPtr = nil then begin
if tlPtr^.next <> nil then
if tlPtr^.next^.token.kind = poundpoundop then
tlPtr^.next := tlPtr^.next^.next;
if tlPtr^.next^.token.kind = poundpoundop then begin
tlPtr := tlPtr^.next;
goto 1;
end; {if}
if lastPtr <> nil then
if lastPtr^.token.kind = poundpoundop then
if tokenList <> nil then
@ -2179,7 +2187,7 @@ else begin
if not tcPtr^.expandEnabled then
inhibit := true;
if tcPtr = pptr^.tokens then
if (mPtr <> nil) and (mPtr^.parameters > 0) then
if (mPtr <> nil) and (mPtr^.parameters >= 0) then
inhibit := true;
if (mPtr <> nil) and (not inhibit) then
Expand(mPtr)
@ -2209,7 +2217,7 @@ else begin
tokenEnd := tlPtr^.tokenEnd;
PutBackToken(tlPtr^.token, expandEnabled, false);
end; {else}
lastPtr := tlPtr;
1: lastPtr := tlPtr;
tlPtr := tlPtr^.next;
end; {while}
end; {else}
@ -2786,8 +2794,12 @@ var
ple: stringListPtr; {pointer to the last element in parameterList}
pnum: integer; {for counting parameters}
tPtr,tk1,tk2: tokenListRecordPtr; {pointer to a token}
luseGlobalPool: boolean; {local copy of useGlobalPool}
begin {DoDefine}
lUseGlobalPool := useGlobalPool;
useGlobalPool := true; {use global memory for defines}
expandMacros := false; {block expansions}
saveNumber := true; {save characters in numeric tokens}
parameterList := nil; {no parameters yet}
@ -2999,6 +3011,7 @@ var
dispose(np);
end; {while}
saveNumber := false; {stop saving numeric strings}
useGlobalPool := lUseGlobalPool;
end; {DoDefine}

View File

@ -34,7 +34,7 @@ second string argument!"))
goto Fail;
strcpy(s1, "this is the first string argument");
strcpy(s1, strncat (s1, s2, -5));
strcpy(s1, strncat (s1, s2, 0));
if (strcmp (s1, "this is the first string argument"))
goto Fail;

View File

@ -29,7 +29,7 @@ int main (void)
if (i != 0)
goto Fail;
i = strncmp (s1, s2, -90L); /* should just return 0 */
i = strncmp (s1, s2, 0L); /* should just return 0 */
if (i != 0)
goto Fail;

View File

@ -5,9 +5,7 @@
void main(void)
{
int i;
i = 4/0;
static int i = 4/0;
printf("Failed Deviance Test 7.6.1.2\n");
}

View File

@ -5,9 +5,7 @@
void main(void)
{
int i;
i = 4%0;
static int i = 4%0;
printf("Failed Deviance Test 7.6.1.3\n");
}

View File

@ -1,11 +1,13 @@
ORCA/C 2.2.0
ORCA/C 2.2.1
Copyright 1997, Byte Works Inc.
Updated by Stephen Heumann and Kelvin Sherlock, 2017-2023
Updated by Stephen Heumann and Kelvin Sherlock, 2017-2024
These release notes document the changes between ORCA/C 2.0 and ORCA/C 2.2.0. They are intended mainly for users familiar with earlier versions of ORCA/C. New users should simply refer to the ORCA/C 2.2 manual, which has been fully updated to document the new features and other changes in ORCA/C 2.2.0.
These release notes document the changes between ORCA/C 2.0 and ORCA/C 2.2.1. They are intended mainly for users familiar with earlier versions of ORCA/C. New users should refer to the ORCA/C 2.2 manual, which has been fully updated to document the new features and other changes in ORCA/C 2.2.
-- Change List --------------------------------------------------------------
2.2.1 1. Bugs squashed. See bug notes, below.
2.2.0 1. Bugs squashed. See bug notes, below.
2. New language features added (mainly features from C99 and C11).
@ -1582,6 +1584,38 @@ Turning this optimization on means ORCA/C is no longer strictly in compliance wi
If you use #pragma debug 0x0010 to enable stack check debug code, the compiler will still flag variable argument functions that do not consume all arguments as a run-time error, even though ANSI C does allow them.
-- Bugs from C 2.2.0 that have been fixed in C 2.2.1 ------------------------
1. If there was a #define directive immediately after the opening brace of a function body, and the macro defined there was used after the end of that function or was used in the initializer for a static variable, incorrect code might be generated or spurious errors might be reported.
(Kelvin Sherlock)
2. In <gsbug.h>, the declarations of the pseudo tool calls provided by GSBug did not include prototypes. Now they do.
(Kelvin Sherlock)
3. In <gsos.h>, the error code devListFull was misspelled as defListFull.
4. Code that divides an integer constant by zero (e.g. 1/0) will no longer always produce an error. Such code will produce undefined behavior if it is executed, but since the compiler cannot always determine whether the code will be executed at run time, this is no longer treated as an error that prevents compilation. If #pragma lint bit 5 is set, a lint message about the division by zero will still be produced. An error will also still be reported for division by zero in constant expressions that need to be evaluated at compile time.
5. In some obscure circumstances, ORCA/C might behave incorrectly when the postfix ++ or -- operators were used on an expression of pointer type. This would typically result in "compiler error" messages, but could potentially also cause incorrect code to be generated without any errors being reported.
6. If the first string passed to strcat() or strncat() crossed a bank boundary, those functions would generally behave incorrectly and corrupt memory.
7. In certain situations where the third argument to strncat() was 0x8000 or greater, it could behave incorrectly.
8. If strncmp() was called with a third argument of 0x1000000 or greater, it could sometimes incorrectly return 0. (Note that such size values exceed the address space of the 65816, so under ORCA/C they effectively impose no limit on the number of characters to be compared. The behavior of strncmp() should therefore be the same as strcmp() in these cases.)
9. If an error was encountered when trying to write out buffered data in fclose(), the stream would not actually be closed, and the buffer allocated for it would not be freed. Now the stream will be closed (disassociated from the file) and the buffer will be freed even if an error is encountered.
10. If an error was encountered when trying to write out buffered data to a file when exiting the program, it could hang and never actually quit.
11. If fclose() was called on a file created by tmpfile() at a time when there was very little free memory available, it could corrupt memory and cause a crash. (This will no longer happen, but the file still may not be deleted if there is insufficient memory available when fclose() is called.)
12. If a numeric token formed using a ## preprocessor operator was an operand for another ## preprocessor operator, the resulting token would be incorrect.
13. If an empty argument was passed for a macro parameter that was used as an operand of the ## preprocessing operator, the result would likely be incorrect, and subsequent uses of the same macro also might not be expanded correctly.
-- Bugs from C 2.1.1 B3 that have been fixed in C 2.2.0 ---------------------
1. There were various bugs that could cause incorrect code to be generated in certain cases. Some of these were specific to certain optimization passes, alone or in combination.