Implement stricter type checks for comparisons.

These rules are used if loose type checks are disabled. They are intended to strictly implement the constraints in C17 sections 6.5.9 and 6.5.10.

This patch also fixes a bug where object pointer comparisons to "const void *" should be permitted but were not.
This commit is contained in:
Stephen Heumann 2021-09-10 21:00:23 -05:00
parent 2614f10ced
commit 7848e50218
2 changed files with 42 additions and 9 deletions

View File

@ -3337,28 +3337,59 @@ var
end; {FunctionCall}
procedure CompareCompatible (var t1,t2: typePtr);
procedure CompareCompatible (var t1,t2: typePtr; equality: boolean);
{ Make sure that it is legal to compare t1 to t2 }
{ }
{ parameters: }
{ t1,t2 - the types to compare }
{ equality - is this for an (in)equality comparison? }
begin {CompareCompatible}
if (t1^.kind = functionType) or (t2^.kind = functionType) then begin
if not CompTypes(t1, t2) then
Error(47)
else if not looseTypeChecks and not equality then
Error(47);
end {if}
else if t1^.kind in [pointerType,arrayType] then begin
if t2^.kind in [pointerType,arrayType] then begin
if (t1^.ptype = voidPtr) or (t2^.ptype = voidPtr) then
else if not CompTypes(t1^.ptype, t2^.ptype) then
if CompTypes(t1^.ptype, t2^.ptype) then begin
if not looseTypeChecks and not equality then
if t1^.ptype^.kind = functionType then
Error(47);
end {if}
else if (t1^.ptype^.kind=scalarType) and (t1^.ptype^.basetype=cgVoid)
then begin
if not looseTypeChecks then begin
if not equality then
Error(47)
else if (not tlastwasconst) or (tlastconst <> 0) then
if t2^.ptype^.kind = functionType then
Error(47);
end {if}
end {else if}
else if (t2^.ptype^.kind=scalarType) and (t2^.ptype^.basetype=cgVoid)
then begin
if not looseTypeChecks then begin
if not equality then
Error(47)
else if (not lastwasconst) or (lastconst <> 0) then
if t1^.ptype^.kind = functionType then
Error(47);
end {if}
end {else if}
else
Error(47);
t2 := ulongPtr;
end {if}
else if (not lastwasconst) or (lastconst <> 0) then
else if (not lastwasconst) or (lastconst <> 0)
or (not equality and not looseTypeChecks) then
Error(47);
t1 := ulongPtr;
end {if}
else if expressionType^.kind in [pointerType,arrayType] then begin
if (not tlastwasconst) or (tlastconst <> 0) then
if (not equality) or (not tlastwasconst) or (tlastconst <> 0) then
Error(47);
t2 := ulongPtr;
end; {else if}
@ -4269,7 +4300,7 @@ case tree^.token.kind of
tlastwasconst := lastwasconst;
tlastconst := lastconst;
GenerateCode(tree^.right);
CompareCompatible(ltype, expressionType);
CompareCompatible(ltype, expressionType, true);
if tree^.token.kind = eqeqop then
Gen0t(pc_equ, UsualBinaryConversions(lType))
else
@ -4284,7 +4315,7 @@ case tree^.token.kind of
GenerateCode(tree^.left);
lType := expressionType;
GenerateCode(tree^.right);
CompareCompatible(ltype, expressionType);
CompareCompatible(ltype, expressionType, false);
if tree^.token.kind = lteqop then
Gen0t(pc_leq, UsualBinaryConversions(lType))
else if tree^.token.kind = gteqop then

View File

@ -475,13 +475,15 @@ Bit 2 (a value of 4) controls whether spurious tokens are allowed after an #endi
Bit 4 (a value of 16) controls whether ORCA/C follows C99-style rules for declaration placement and block scopes. See "New Language Features," above.
Bit 5 (a value of 32) controls whether type compatibility checks should strictly follow the C standards, or whether looser rules should be used in certain cases. If this bit is set, the looser rules will be followed, matching ORCA/C's historical behavior. Bit 5 is currently set by default, but new code should avoid relying on this. There are three specific situations where bit 5 currently has an effect:
Bit 5 (a value of 32) controls whether type compatibility checks should strictly follow the C standards, or whether looser rules should be used in certain cases. If this bit is set, the looser rules will be followed, matching ORCA/C's historical behavior. Bit 5 is currently set by default, but new code should avoid relying on this. There are four specific situations where bit 5 currently has an effect:
First, setting bit 5 causes pointer assignments that discard type qualifiers to be allowed. For example, this affects an assignment from an expression of type "const int *" to a variable of type "int *", because it discards the "const" qualifier from the type pointed to. These assignments are prohibited by the C standards, but ORCA/C historically allowed them. If bit 5 is set it will still allow them, but if bit 5 is clear it will give an error.
Second, setting bit 5 causes type compatibility checks involving function pointers to ignore the prototyped parameter types. If bit 5 is clear, the prototyped parameter types (if available) must be compatible.
Third, setting bit 5 causes the types "char" and "unsigned char" to be treated as compatible with each other for most purposes. These types have the same representation in ORCA/C, but the C standards specify that they are nonetheless two distinct types and are not mutually compatible. Therefore, any standard-conforming C compiler should produce a diagnostic message if these two types or types derived from them are used in a situation where the types are required to be compatible, as in the following example:
Third, setting bit 5 causes certain comparisons involving pointers to be permitted even though they violate constraints specified in the C standards. If bit 5 is clear, the rules in the standards will be followed strictly.
Fourth, setting bit 5 causes the types "char" and "unsigned char" to be treated as compatible with each other for most purposes. These types have the same representation in ORCA/C, but the C standards specify that they are nonetheless two distinct types and are not mutually compatible. Therefore, any standard-conforming C compiler should produce a diagnostic message if these two types or types derived from them are used in a situation where the types are required to be compatible, as in the following example:
unsigned char uc;
char *p = &uc; /* &uc has type unsigned char *, incompatible with char *. */