diff --git a/Expression.pas b/Expression.pas index 75f1372..57373e4 100644 --- a/Expression.pas +++ b/Expression.pas @@ -65,6 +65,7 @@ var {----} lastwasconst: boolean; {did the last GenerateCode result in an integer constant?} lastconst: longint; {last integer constant from GenerateCode} + lastWasNullPtrConst: boolean; {did last GenerateCode give a null ptr const?} {---------------------------------------------------------------} procedure AssignmentConversion (t1, t2: typePtr; isConstant: boolean; @@ -570,6 +571,25 @@ if tree <> nil then begin end; {DisposeTree} +procedure ValueExpressionConversions; + +{ Perform type conversions applicable to an expression used } +{ for its value. These include lvalue conversion (removing } +{ qualifiers), array-to-pointer conversion, and } +{ function-to-pointer conversion. See C17 section 6.3.2.1. } +{ } +{ variables: } +{ expressionType - set to type after conversions } + +begin {ValueExpressionConversions} +expressionType := Unqualify(expressionType); +if expressionType^.kind = arrayType then + expressionType := MakePointerTo(expressionType^.aType) +else if expressionType^.kind = functionType then + expressionType := MakePointerTo(expressionType); +end; {ValueExpressionConversions} + + procedure AssignmentConversion {t1, t2: typePtr; isConstant: boolean; value: longint; genCode, checkConst: boolean}; @@ -2772,7 +2792,7 @@ var doingScalar: boolean; {temp; for assignment operators} et: baseTypeEnum; {temp storage for a base type} i: integer; {loop variable} - isString: boolean; {was the ? : a string?} + isNullPtrConst: boolean; {is this a null pointer constant?} isVolatile: boolean; {is this a volatile op?} lType: typePtr; {type of operands} kind: typeKind; {temp type kind} @@ -2780,6 +2800,7 @@ var t1: integer; {temporary work space label number} tlastwasconst: boolean; {temp lastwasconst} tlastconst: longint; {temp lastconst} + tlastWasNullPtrConst: boolean; {temp lastWasNullPtrConst} tp: tokenPtr; {work pointer} tType: typePtr; {temp type of operand} @@ -3528,6 +3549,7 @@ var begin {GenerateCode} lastwasconst := false; +isNullPtrConst := false; case tree^.token.kind of parameterOper: @@ -3590,6 +3612,7 @@ case tree^.token.kind of Gen1t(pc_ldc, tree^.token.ival, cgWord); lastwasconst := true; lastconst := tree^.token.ival; + isNullPtrConst := tree^.token.ival = 0; if tree^.token.kind = intConst then expressionType := intPtr else if tree^.token.kind = uintConst then @@ -3612,6 +3635,7 @@ case tree^.token.kind of expressionType := ulongPtr; lastwasconst := true; lastconst := tree^.token.lval; + isNullPtrConst := tree^.token.lval = 0; end; {case longConst} longlongConst,ulonglongConst: begin @@ -3624,6 +3648,7 @@ case tree^.token.kind of lastwasconst := true; lastconst := tree^.token.qval.lo; end; {if} + isNullPtrConst := (tree^.token.qval.hi = 0) and (tree^.token.qval.lo = 0); end; {case longlongConst} floatConst: begin @@ -4565,78 +4590,71 @@ case tree^.token.kind of GenerateCode(tree^.left); {evaluate the condition} CompareToZero(pc_neq); GenerateCode(tree^.middle); {evaluate true expression} + ValueExpressionConversions; lType := expressionType; - tlastwasconst := lastwasconst; - tlastconst := lastconst; + tlastWasNullPtrConst := lastWasNullPtrConst; GenerateCode(tree^.right); {evaluate false expression} - isString := false; {handle string operands} - if lType^.kind in [arrayType,pointerType] then - if lType^.aType^.baseType = cgUByte then begin - with expressionType^ do - if kind in [arrayType,pointerType] then begin - if aType^.baseType = cgUByte then - isString := true - else if (kind = pointerType) - and (CompTypes(lType,expressionType)) then - {it's all OK} - else - Error(47) - end {if} - else if (kind = scalarType) - and lastWasConst - and (lastConst = 0) then - et := UsualBinaryConversions(lType) - {it's all OK} - else + ValueExpressionConversions; + {check, compute, and convert types} + if (lType^.kind = pointerType) or (expressionType^.kind = pointerType) + then begin + if tlastWasNullPtrConst then begin + if lType^.kind = scalarType then + Gen2(pc_cnn, ord(lType^.baseType), ord(cgULong)); + end {if} + else if lastWasNullPtrConst then begin + if expressionType^.kind = scalarType then + Gen2(pc_cnv, ord(expressionType^.baseType), ord(cgULong)); + expressionType := lType; + end {if} + else if lType^.kind <> expressionType^.kind then {not both pointers} + Error(47) + else if IsVoid(lType^.pType) or IsVoid(expressionType^.pType) then begin + if not looseTypeChecks then + if (lType^.pType^.kind = functionType) or + (expressionType^.pType^.kind = functionType) then Error(47); - lType := voidPtrPtr; - expressionType := voidPtrPtr; - end; {if} - with expressionType^ do - if kind in [arrayType,pointerType] then - if aType^.baseType in [cgByte,cgUByte] then begin - if kind = pointerType then begin - if tlastwasconst and (tlastconst = 0) then - {it's all OK} - else if CompTypes(lType, expressionType) then - {it's all OK} - else - Error(47); - end {if} - else + expressionType := MakePointerTo(MakeQualifiedType(voidPtr, + lType^.pType^.qualifiers+expressionType^.pType^.qualifiers)); + end {else if} + else if CompTypes(Unqualify(lType^.pType), + Unqualify(expressionType^.pType)) then begin + if not looseTypeChecks then + if not StrictCompTypes(Unqualify(lType^.pType), + Unqualify(expressionType^.pType)) then Error(47); - et := UsualBinaryConversions(lType); - lType := voidPtrPtr; - expressionType := voidPtrPtr; - end; {if} - {generate the operation} - if lType^.kind in [structType, unionType, arrayType] then begin + expressionType := MakePointerTo(MakeQualifiedType(MakeCompositeType( + Unqualify(lType^.pType),Unqualify(expressionType^.pType)), + lType^.pType^.qualifiers+expressionType^.pType^.qualifiers)); + end {else if} + else + Error(47); + et := cgULong; + end {if} + else if lType^.kind in [structType, unionType] then begin if not CompTypes(lType, expressionType) then Error(47); - Gen0(pc_bno); - Gen0t(pc_tri, cgULong); + et := cgULong; end {if} else begin - if expressionType^.kind = pointerType then - tType := expressionType - else - tType := lType; - if (expressionType^.kind = scalarType) - and (expressionType^.baseType = cgVoid) - and (lType^.kind = scalarType) - and (lType^.baseType = cgVoid) then + if IsVoid(lType) and IsVoid(expressionType) then et := cgVoid else et := UsualBinaryConversions(lType); - Gen0(pc_bno); - Gen0t(pc_tri, et); end; {else} - if isString then {set the type for strings} - expressionType := stringTypePtr; + {generate the operation} + Gen0(pc_bno); + Gen0t(pc_tri, et); end; {case colonch} castoper: begin {(cast)} GenerateCode(tree^.left); + if lastWasNullPtrConst then + if expressionType^.kind = scalarType then + if tree^.castType^.kind = pointerType then + if IsVoid(tree^.castType^.pType) then + if tree^.castType^.pType^.qualifiers = [] then + isNullPtrConst := true; Cast(tree^.castType); end; {case castoper} @@ -4646,6 +4664,7 @@ case tree^.token.kind of end; {case} if doDispose then dispose(tree); +lastWasNullPtrConst := isNullPtrConst; end; {GenerateCode} diff --git a/Symbol.pas b/Symbol.pas index d5fccf6..0939667 100644 --- a/Symbol.pas +++ b/Symbol.pas @@ -164,6 +164,16 @@ procedure InitSymbol; { Initialize the symbol table module } +function IsVoid (tp: typePtr): boolean; + +{ Check to see if a type is void } +{ } +{ Parameters: } +{ tp - type to check } +{ } +{ Returns: True if the type is void, else false } + + function LabelToDisp (lab: integer): integer; extern; { convert a local label number to a stack frame displacement } @@ -192,6 +202,17 @@ function MakePointerTo (pType: typePtr): typePtr; { returns: the pointer type } +function MakeCompositeType (t1, t2: typePtr): typePtr; + +{ Make the composite type of two compatible types. } +{ See C17 section 6.2.7. } +{ } +{ parameters: } +{ t1,t2 - the input types (must be compatible) } +{ } +{ returns: pointer to the composite type } + + function MakeQualifiedType (origType: typePtr; qualifiers: typeQualifierSet): typePtr; @@ -427,26 +448,6 @@ var p1, p2: parameterPtr; {for tracing parameter lists} pt1,pt2: typePtr; {pointer types} - - function IsVoid (tp: typePtr): boolean; - - { Check to see if a type is void } - { } - { Parameters: } - { tp - type to check } - { } - { Returns: True if the type is void, else false } - - begin {IsVoid} - IsVoid := false; - if tp = voidPtr then - IsVoid := true - else if tp^.kind = scalarType then - if tp^.baseType = cgVoid then - IsVoid := true; - end; {IsVoid} - - begin {CompTypes} CompTypes := false; {assume the types are not compatible} kind1 := t1^.kind; {get these for efficiency} @@ -1703,6 +1704,122 @@ constCharPtr^.qualifiers := [tqConst]; end; {InitSymbol} +function IsVoid {tp: typePtr): boolean}; + +{ Check to see if a type is void } +{ } +{ Parameters: } +{ tp - type to check } +{ } +{ Returns: True if the type is void, else false } + +begin {IsVoid} +IsVoid := false; +if tp = voidPtr then + IsVoid := true +else if tp^.kind = scalarType then + if tp^.baseType = cgVoid then + IsVoid := true; +end; {IsVoid} + + +function CopyType (tp: typePtr): typePtr; + +{ Make a new copy of a type, so it can be modified. } +{ } +{ Parameters: } +{ tp - type to copy } +{ } +{ Returns: The new copy of the type } + +var + tType: typePtr; {the new copy of the type} + p1,p2: parameterPtr; {parameter ptrs for copying prototypes} + pPtr: ^parameterPtr; {temp for copying prototypes} + +begin {CopyType} +if tp^.kind in [structType,unionType] then + Error(57); +tType := pointer(Malloc(sizeof(typeRecord))); +tType^ := tp^; {copy type record} +tType^.saveDisp := 0; +if tp^.kind = functionType then {copy prototype parameter list} + if tp^.prototyped then begin + p1 := tp^.parameterList; + pPtr := @tType^.parameterList; + while p1 <> nil do begin + p2 := pointer(Malloc(sizeof(parameterRecord))); + p2^ := p1^; + pPtr^ := p2; + pPtr := @p2^.next; + p1 := p1^.next; + end; {while} + end; {if} +CopyType := tType; +end; {CopyType} + + +function MakeCompositeType {t1, t2: typePtr): typePtr}; + +{ Make the composite type of two compatible types. } +{ See C17 section 6.2.7. } +{ } +{ parameters: } +{ t1,t2 - the input types (should be compatible) } +{ } +{ returns: pointer to the composite type } + +var + compType: typePtr; {the composite type} + tType: typePtr; {temp type} + p1,p2: parameterPtr; {parameter ptrs for handling prototypes} + +begin {MakeCompositeType} +compType := t2; {default to t2} +if t1 <> t2 then + if t1^.kind = t2^.kind then begin + if t2^.kind = functionType then {switch fn types if only t1 is prototyped} + if not t2^.prototyped then + if t1^.prototyped then begin + compType := t1; + t1 := t2; + t2 := compType; + end; {if} + {apply recursively for derived types} + if t2^.kind in [arrayType,pointerType,functionType] then begin + tType := MakeCompositeType(t1^.aType,t2^.aType); + if tType <> t2^.aType then begin + compType := CopyType(compType); + compType^.aType := tType; + end; {if} + end; {if} + if t2^.kind = arrayType then {get array size from t1 if needed} + if t2^.size = 0 then + if t1^.size <> 0 then + if t1^.aType^.size = t2^.aType^.size then begin + if compType = t2 then + compType := CopyType(t2); + CompType^.size := t1^.size; + CompType^.elements := t1^.elements; + end; {if} + if t2^.kind = functionType then {compose function parameter types} + if t1^.prototyped and t2^.prototyped then begin + if compType = t2 then + compType := CopyType(t2); + p1 := t1^.parameterList; + p2 := compType^.parameterList; + while (p1 <> nil) and (p2 <> nil) do begin + p2^.parameterType := + MakeCompositeType(p1^.parameterType,p2^.parameterType); + p1 := p1^.next; + p2 := p2^.next; + end; {while} + end; + end; {if} +MakeCompositeType := compType; +end; {MakeCompositeType} + + function MakePascalType {origType: typePtr): typePtr}; { make a version of a type with the pascal qualifier applied } diff --git a/Tests/Conformance/DOIT3 b/Tests/Conformance/DOIT3 index 1a5fb1b..e731ef3 100644 --- a/Tests/Conformance/DOIT3 +++ b/Tests/Conformance/DOIT3 @@ -28,3 +28,4 @@ {1} c11sassert.c {1} c11unicode.c {1} c11uchar.c +{1} c11ternary.c diff --git a/Tests/Conformance/c11ternary.c b/Tests/Conformance/c11ternary.c new file mode 100644 index 0000000..7aa4e47 --- /dev/null +++ b/Tests/Conformance/c11ternary.c @@ -0,0 +1,57 @@ +/* + * Test the ? : operator. + * + * The basic properties tested should hold back to C89, + * but a C11 feature (_Generic) is used to test them. + */ + +#define assert_type(e,t) (void)_Generic((e), t:(e)) + +int main(void) { + int i = 1; + long l = 2; + double d = 3; + struct S {int i;} s = {4}; + const struct S t = {5}; + const void *cvp = &i; + void *vp = &i; + const int *cip = &i; + volatile int *vip = 0; + int *ip = &i; + const char *ccp = 0; + int (*fp1)() = 0; + int (*fp2)(int (*)[40]) = 0; + int (*fp3)(int (*)[]) = 0; + + assert_type(1?i:l, long); + assert_type(1?d:i, double); + assert_type(1?s:t, struct S); + 1?(void)2:(void)3; + assert_type(1?ip:ip, int *); + assert_type(1?ip:cip, const int *); + assert_type(1?cip:ip, const int *); + assert_type(1?0:ip, int *); + assert_type(0?0LL:ip, int *); + assert_type(1?(void*)0:ip, int *); + assert_type(1?cip:0, const int *); + assert_type(1?cip:0LL, const int *); + assert_type(1?cip:(char)0.0, const int *); + assert_type(1?cip:(void*)0, const int *); + assert_type(1?(void*)(void*)0:ip, void *); + assert_type(1?(void*)ip:ip, void *); + assert_type(1?cip:(void*)(void*)0, const void *); + assert_type(1?(void*)ip:cip, const void *); + assert_type(1?main:main, int(*)(void)); + assert_type(1?main:0, int(*)(void)); + assert_type(1?(const void*)cip:(void*)ip, const void *); + assert_type(1?cvp:cip, const void *); + assert_type(1?vip:0, volatile int *); + assert_type(1?cip:vip, const volatile int *); + assert_type(1?vp:ccp, const void *); + assert_type(1?ip:cip, const int *); + assert_type(1?vp:ip, void *); + assert_type(1?fp1:fp2, int (*)(int (*)[40])); + assert_type(1?fp2:fp3, int (*)(int (*)[40])); + assert_type(1?fp2:0, int (*)(int (*)[40])); + assert_type(1?fp2:(void*)0, int (*)(int (*)[40])); +} diff --git a/Tests/Deviance/D7.8.0.1.CC b/Tests/Deviance/D7.8.0.1.CC new file mode 100644 index 0000000..7724063 --- /dev/null +++ b/Tests/Deviance/D7.8.0.1.CC @@ -0,0 +1,27 @@ +/* Deviance Test 7.8.0.1: Ensure invalid operand types for ?: are detected */ + +int printf(const char *, ...); + +int main(void) { + int i = 1; + struct S {int i;} s = {4}; + int *ip = &i; + long *lp = 0; + const int *cip = &i; + + /* each statement below should give an error */ + 1 ? i : s; + 1 ? s : i; + 1 ? i : (void)0; + 1 ? ip : lp; + + /* these are illegal in standard C, but allowed by loose type checks */ +#pragma ignore 24 + 1 ? main : (void*)(void*)0; + 1 ? &ip : &cip; + + /* should give an error, but currently does not in ORCA/C */ + 1 ? cip : (char)+0.0; + + printf ("Failed Deviance Test 7.8.0.1\n"); +} diff --git a/Tests/Deviance/DOIT b/Tests/Deviance/DOIT index 534f8be..aa5eb2f 100644 --- a/Tests/Deviance/DOIT +++ b/Tests/Deviance/DOIT @@ -51,6 +51,7 @@ {1} D7.6.6.1.CC {1} D7.6.7.1.CC {1} D7.6.8.1.CC +{1} D7.8.0.1.CC {1} D8.7.0.1.CC {1} D8.8.0.1.CC {1} D9.2.0.1.CC diff --git a/cc.notes b/cc.notes index 259c611..462690c 100644 --- a/cc.notes +++ b/cc.notes @@ -584,7 +584,7 @@ First, setting bit 5 causes pointer assignments that discard type qualifiers to 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 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. +Third, setting bit 5 causes certain comparisons involving pointers, as well as certain uses of the ? : operator with pointer operands, 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 ORCA/C to treat basic types with the same representation as mutually compatible. This affects the following pairs of types: short and int, unsigned short and unsigned int, char and unsigned char. Historically, ORCA/C essentially treated each of these pairs as being the same type, so it never reported type conflicts between them. If bit 5 is set, it will continue to do so. If bit 5 is clear, it will treat all of the above types as distinct and mutually incompatible, as specified by the C standards. @@ -1825,6 +1825,10 @@ int foo(int[42]); (Devin Reade) +189. Some combinations of operand types were not properly supported for the ? : operator. This could result in a spurious error, or could cause the ? : expression to have an incorrect type. One case where a spurious error would be produced is for expressions like i?0:p, where p is a pointer. + +(Jay Krell, Devin Reade) + -- Bugs from C 2.1.0 that have been fixed ----------------------------------- 1. In some situations, fread() reread the first 1K or so of the file.