diff --git a/CCommon.pas b/CCommon.pas index 3fec42f..27d753d 100644 --- a/CCommon.pas +++ b/CCommon.pas @@ -373,7 +373,7 @@ type pnext: identPtr); {next parameter} external: (); global,private: (); - none: (); + none: (anonMemberField: boolean); {field from an anonymous struct/union member?} end; {mini-assembler} diff --git a/Header.pas b/Header.pas index 50ce175..26ab68f 100644 --- a/Header.pas +++ b/Header.pas @@ -18,7 +18,7 @@ uses CCommon, MM, Scanner, Symbol, CGI; {$segment 'SCANNER'} const - symFileVersion = 29; {version number of .sym file format} + symFileVersion = 30; {version number of .sym file format} var inhibitHeader: boolean; {should .sym includes be blocked?} @@ -1104,6 +1104,8 @@ procedure EndInclude {chPtr: ptr}; WriteByte(ord(ip^.isForwardDeclared)); WriteByte(ord(ip^.class)); WriteByte(ord(ip^.storage)); + if ip^.storage = none then + WriteByte(ord(ip^.anonMemberField)); end; {WriteIdent} @@ -1803,6 +1805,8 @@ var sp^.isForwardDeclared := boolean(ReadByte); sp^.class := tokenEnum(ReadByte); sp^.storage := storageType(ReadByte); + if sp^.storage = none then + sp^.anonMemberField := boolean(ReadByte); ReadIdent := sp; end; {ReadIdent} diff --git a/Parser.pas b/Parser.pas index 7e76491..b160cab 100644 --- a/Parser.pas +++ b/Parser.pas @@ -2422,7 +2422,8 @@ var while i <> 0 do begin ip := tp^.fieldList; while ip <> nil do begin - Fill(1, ip^.iType); + if not ip^.anonMemberField then + Fill(1, ip^.iType); ip := ip^.next; end; {while} i := i-1; @@ -2648,8 +2649,11 @@ var { writeln('Initializer: ', ip^.bitsize:10, ip^.bitdisp:10, bitCount:10); {debug} if kind = unionType then ip := nil - else + else begin ip := ip^.next; + while (ip <> nil) and ip^.anonMemberField do + ip := ip^.next; + end; {else} if token.kind = commach then begin if ip <> nil then NextToken; @@ -2813,6 +2817,39 @@ var variable: identPtr; {variable being defined} didFlexibleArray: boolean; {have we seen a flexible array member?} fieldDeclSpecifiers: declSpecifiersRecord; {decl specifiers for field} + tPtr: typePtr; {for building types} + anonMember: boolean; {processing an anonymous struct/union?} + + procedure AddField(variable: identPtr; anonMemberField: boolean); + + { add a field to the field list } + { } + { parameters } + { variable - field to add } + { checkDups - check for duplicate-named fields } + { anonMemberField - is this a field from an anonymous } + { struct/union member? } + + label 1; + + var + tfl: identPtr; {for traversing field list} + + begin {AddField} + if variable^.name^ <> '~anonymous' then begin + tfl := fl; {(check for dups)} + while tfl <> nil do begin + if tfl^.name^ = variable^.name^ then begin + Error(42); + goto 1; + end; {if} + tfl := tfl^.next; + end; {while} + end; {if} +1: variable^.next := fl; + variable^.anonMemberField := anonMemberField; + fl := variable; + end; {AddField} begin {FieldList} ldoingParameters := doingParameters; {allow fields in K&R dec. area} @@ -2830,124 +2867,138 @@ var goto 1; end; {if} DeclarationSpecifiers(fieldDeclSpecifiers, specifierQualifierListElement, ident); - if not skipDeclarator then - repeat {declare the variables...} - if didFlexibleArray then - Error(118); - variable := nil; - if token.kind <> colonch then begin - Declarator(fieldDeclSpecifiers, variable, fieldListSpace, false); - if variable <> nil then {enter the var in the field list} - begin - tfl := fl; {(check for dups)} - while tfl <> nil do begin - if tfl^.name^ = variable^.name^ then - Error(42); - tfl := tfl^.next; - end; {while} - variable^.next := fl; - fl := variable; + repeat {declare the variables...} + if didFlexibleArray then + Error(118); + variable := nil; + anonMember := false; + if token.kind <> colonch then begin + if (token.kind = semicolonch) then begin + tPtr := fieldDeclSpecifiers.typeSpec; + while tPtr^.kind = definedType do + tPtr := tPtr^.dType; + if (tPtr^.kind in [structType,unionType]) + and (tPtr^.sName = nil) + and ((structsy in fieldDeclSpecifiers.declarationModifiers) + or (unionsy in fieldDeclSpecifiers.declarationModifiers)) + then begin + variable := NewSymbol(@'~anonymous', tPtr, ident, + fieldListSpace, defined); + anonMember := true; end; {if} - end; {if} - if kind = unionType then begin - disp := 0; - bitdisp := 0; - end; {if} - if token.kind = colonch then {handle a bit field} - begin - NextToken; - Expression(arrayExpression,[commach,semicolonch]); - if (expressionValue >= maxBitField) or (expressionValue < 0) then - begin - Error(54); - expressionValue := maxBitField-1; - end; {if} - if (bitdisp+long(expressionValue).lsw > maxBitField) - or (long(expressionValue).lsw = 0) then begin - disp := disp+((bitDisp+7) div 8); - bitdisp := 0; - if long(expressionValue).lsw = 0 then - if variable <> nil then - Error(55); - end; {if} - if variable <> nil then begin - variable^.disp := disp; - variable^.bitdisp := bitdisp; - variable^.bitsize := long(expressionValue).lsw; - tPtr := variable^.itype; - end {if} - else - tPtr := fieldDeclSpecifiers.typeSpec; - bitdisp := bitdisp+long(expressionValue).lsw; - if kind = unionType then - if ((bitDisp+7) div 8) > maxDisp then - maxDisp := ((bitDisp+7) div 8); - if (tPtr^.kind <> scalarType) - or not (tPtr^.baseType in - [cgByte,cgUByte,cgWord,cgUWord,cgLong,cgULong]) - or (expressionValue > tPtr^.size*8) - or ((expressionValue > 1) and (tPtr^.cType = ctBool)) then - Error(115); - if _Alignassy in fieldDeclSpecifiers.declarationModifiers then - Error(142); end {if} - else if variable <> nil then begin - if bitdisp <> 0 then begin - disp := disp+((bitDisp+7) div 8); - bitdisp := 0; - end; {if} + else + Declarator(fieldDeclSpecifiers, variable, fieldListSpace, false); + if variable <> nil then {enter the var in the field list} + AddField(variable, false); + end; {if} + if kind = unionType then begin + disp := 0; + bitdisp := 0; + end; {if} + if token.kind = colonch then {handle a bit field} + begin + NextToken; + Expression(arrayExpression,[commach,semicolonch]); + if (expressionValue >= maxBitField) or (expressionValue < 0) then + begin + Error(54); + expressionValue := maxBitField-1; + end; {if} + if (bitdisp+long(expressionValue).lsw > maxBitField) + or (long(expressionValue).lsw = 0) then begin + disp := disp+((bitDisp+7) div 8); + bitdisp := 0; + if long(expressionValue).lsw = 0 then + if variable <> nil then + Error(55); + end; {if} + if variable <> nil then begin variable^.disp := disp; variable^.bitdisp := bitdisp; - variable^.bitsize := 0; - disp := disp + variable^.itype^.size; - if disp > maxDisp then - maxDisp := disp; - if variable^.itype^.size = 0 then - if (variable^.itype^.kind = arrayType) - and (disp > 0) then begin {handle flexible array member} - didFlexibleArray := true; - tp^.flexibleArrayMember := true; - end {if} - else - Error(117); + variable^.bitsize := long(expressionValue).lsw; + tPtr := variable^.itype; end {if} - else - Error(116); - - if variable <> nil then {check for a const member} - tPtr := variable^.itype else tPtr := fieldDeclSpecifiers.typeSpec; - while tPtr^.kind in [definedType,arrayType] do begin - if tqConst in tPtr^.qualifiers then - tp^.constMember := true; - if tPtr^.kind = definedType then - tPtr := tPtr^.dType - else {if tPtr^.kind = arrayType then} - tPtr := tPtr^.aType; - end; {while} + bitdisp := bitdisp+long(expressionValue).lsw; + if kind = unionType then + if ((bitDisp+7) div 8) > maxDisp then + maxDisp := ((bitDisp+7) div 8); + if (tPtr^.kind <> scalarType) + or not (tPtr^.baseType in + [cgByte,cgUByte,cgWord,cgUWord,cgLong,cgULong]) + or (expressionValue > tPtr^.size*8) + or ((expressionValue > 1) and (tPtr^.cType = ctBool)) then + Error(115); + if _Alignassy in fieldDeclSpecifiers.declarationModifiers then + Error(142); + end {if} + else if variable <> nil then begin + if bitdisp <> 0 then begin + disp := disp+((bitDisp+7) div 8); + bitdisp := 0; + end; {if} + variable^.disp := disp; + variable^.bitdisp := bitdisp; + variable^.bitsize := 0; + if anonMember then begin + tfl := variable^.itype^.fieldList; + while tfl <> nil do begin + ufl := pointer(Malloc(sizeof(identRecord))); + ufl^ := tfl^; + AddField(ufl, true); + ufl^.disp := ufl^.disp + disp; + tfl := tfl^.next; + end; {while} + end; {if} + disp := disp + variable^.itype^.size; + if disp > maxDisp then + maxDisp := disp; + if variable^.itype^.size = 0 then + if (variable^.itype^.kind = arrayType) + and (disp > 0) then begin {handle flexible array member} + didFlexibleArray := true; + tp^.flexibleArrayMember := true; + end {if} + else + Error(117); + end {if} + else + Error(116); + + if variable <> nil then {check for a const member} + tPtr := variable^.itype + else + tPtr := fieldDeclSpecifiers.typeSpec; + while tPtr^.kind in [definedType,arrayType] do begin if tqConst in tPtr^.qualifiers then tp^.constMember := true; - if tPtr^.kind in [structType,unionType] then begin - if tPtr^.constMember then - tp^.constMember := true; - if tPtr^.flexibleArrayMember then - if kind = structType then - Error(169) - else {if kind = unionType then} - tp^.flexibleArrayMember := true; - end; {if} + if tPtr^.kind = definedType then + tPtr := tPtr^.dType + else {if tPtr^.kind = arrayType then} + tPtr := tPtr^.aType; + end; {while} + if tqConst in tPtr^.qualifiers then + tp^.constMember := true; + if tPtr^.kind in [structType,unionType] then begin + if tPtr^.constMember then + tp^.constMember := true; + if tPtr^.flexibleArrayMember then + if kind = structType then + Error(169) + else {if kind = unionType then} + tp^.flexibleArrayMember := true; + end; {if} - if token.kind = commach then {allow repeated declarations} - begin - NextToken; - done := false; - end {if} - else - done := true; - until done or (token.kind = eofsy) - else - Error(116); + if token.kind = commach then {allow repeated declarations} + begin + NextToken; + done := false; + end {if} + else + done := true; + until done or (token.kind = eofsy); Match(semicolonch,22); {insist on a closing ';'} end; {while} if fl <> nil then begin @@ -3359,6 +3410,10 @@ while token.kind in allowedTokens do begin useGlobalPool := lUseGlobalPool; myTypeSpec := structTypePtr; mySkipDeclarator := token.kind = semicolonch; + if tKind = structType then + myDeclarationModifiers := myDeclarationModifiers + [structsy] + else + myDeclarationModifiers := myDeclarationModifiers + [unionsy]; typeDone := true; end; @@ -4626,8 +4681,11 @@ var end; {if} if union then fp := nil - else + else begin fp := fp^.next; + while (fp <> nil) and fp^.anonMemberField do + fp := fp^.next; + end; {else} end; {while} end; {else} disp := endDisp; diff --git a/Symbol.pas b/Symbol.pas index b4e979d..9b12fbc 100644 --- a/Symbol.pas +++ b/Symbol.pas @@ -1288,7 +1288,8 @@ var if ip = nil then ip := defaultStruct^.fieldList; while ip <> nil do begin - GenSymbol(ip, none); + if ip^.name^ <> '~anonymous' then + GenSymbol(ip, none); ip := ip^.next; end; {while} end; {ExpandStructType} @@ -2116,7 +2117,9 @@ if needSymbol then begin else p^.next := nil; end; {if} -if class in [autosy,registersy] then {check and set the storage class} +if space = fieldListSpace then {check and set the storage class} + p^.storage := none +else if class in [autosy,registersy] then begin if doingFunction or doingParameters then begin p^.storage := stackFrame; diff --git a/Tests/Conformance/DOIT3 b/Tests/Conformance/DOIT3 index e731ef3..a5e2ae8 100644 --- a/Tests/Conformance/DOIT3 +++ b/Tests/Conformance/DOIT3 @@ -29,3 +29,4 @@ {1} c11unicode.c {1} c11uchar.c {1} c11ternary.c +{1} c11anonsu.c diff --git a/Tests/Conformance/c11anonsu.c b/Tests/Conformance/c11anonsu.c new file mode 100644 index 0000000..9ba786d --- /dev/null +++ b/Tests/Conformance/c11anonsu.c @@ -0,0 +1,75 @@ +/* + * Test anonymous structures and unions (C11). + */ + +#include +#include + +struct S { + int a; + union { + volatile struct { + long b; + char c; + }; + double d; + }; +} s1 = {1,2,3}; + +struct T { + int a; + union { + volatile struct { + long b; + char c; + } s; + double d; + } u; +}; + +int main(void) { + struct S *s1p = &s1; + + if (s1.a != 1) + goto Fail; + if (s1.b != 2) + goto Fail; + if (s1.c != 3) + goto Fail; + + s1.d = 123.5; + if (s1p->d != 123.5) + goto Fail; + + struct S s2 = {4,5,6}; + struct S *s2p = &s2; + + if (s2.a != 4) + goto Fail; + if (s2.b != 5) + goto Fail; + if (s2.c != 6) + goto Fail; + + s2.d = 123.5; + if (s2p->d != 123.5) + goto Fail; + + if (sizeof(struct S) != sizeof(struct T)) + goto Fail; + + if (offsetof(struct S, a) != offsetof(struct T, a)) + goto Fail; + if (offsetof(struct S, b) != offsetof(struct T, u.s.b)) + goto Fail; + if (offsetof(struct S, c) != offsetof(struct T, u.s.c)) + goto Fail; + if (offsetof(struct S, d) != offsetof(struct T, u.d)) + goto Fail; + + printf ("Passed Conformance Test c11anonsu\n"); + return 0; + +Fail: + printf ("Failed Conformance Test c11anonsu\n"); +} diff --git a/cc.notes b/cc.notes index 5ec1e90..2bb493b 100644 --- a/cc.notes +++ b/cc.notes @@ -562,6 +562,23 @@ Compound literals can also be used in initializers, as in the following declarat (This declaration could be used inside or outside of a function, with the compound literal having automatic or static storage duration, respectively.) +27. (C11) ORCA/C now supports anonymous structures and unions. These are unnamed members of a structure or union that themselves have structure or union type. Their structure or union type must be declared directly within the declaration of the containing structure or union type (not using a typedef) and must not have a tag. Anonymous structures or unions may be nested. + +Here is an example of a structure containing an anonymous union that in turn contains an anonymous structure: + + struct S { + int a; + union { + struct { + long b; + char c; + }; + double d; + }; + } s; + +Members of anonymous structures or unions (including nested ones) may be accessed as if they are members of the containing structure or union. For example, given the declaration above, the expressions s.a, s.b, s.c, and s.d may be used to access the various fields. All the fields must have distinct names. + Multi-Character Character Constants -----------------------------------