v0.2.1
Pre-releaseAdded
-
The C type universe in
rucc-types, interned, so that two spellings of the same type are the same four byte id and type equality is an integer comparison rather than a structural walk. A type keeps its sugar: atypedefnode remembers the name it was written as, and its canonical form is stored beside it, so a diagnostic can printsize_twhere the user wrotesize_twhile the rules that need the type behind it ask for the canonical id and get an answer without walking anything. Canonicalisation reaches below the outermost node, so a pointer to a typedef of an array canonicalises to a pointer to the array, and aconston a typedef of an array lands on the element the way the standard says it does. -
_Atomicis a type rather than a qualifier, which is the shape the standard describes and also the only shape that gets the layout right. A sixteen byte structure is aligned to eight and_Atomicof it is aligned to sixteen, so a type system that treated the two as one type with a flag set would disagree with itself about where the object goes. -
Layout computation driven by the target description rather than by the host, for every type except records.
longis four bytes on Windows and eight on Linux,long doubleis eight bytes on Apple and sixteen on SysV x86-64, and a_Complexis two of its component with the component's own alignment._BitIntis laid out like the smallest standard integer until it outgrows one and then like an array of a granule, and the granule is 64 bits on x86-64 and RISC-V and 128 on AArch64, which is a new field in the target description rather than a#[cfg]. Every one of these numbers was measured against GCC 13 on x86-64 Linux and clang on AArch64 Darwin rather than recalled. A type with no size says which kind of no size it has, because an incomplete type, a function type and an array too large for the address space are three different diagnostics. -
Record layout in
rucc-types: member offsets, bit-field packing, zero width bit-fields,packed,#pragma pack,alignedon a member and on the record, anonymous members and flexible array members. Whoever walks the members hands them tolayout_recordand gets back an offset for each one, in bits, so a bit-field and an ordinary member are described the same way. Every rule was measured against gcc 13.3 on x86-64 Linux and clang on AArch64 Darwin over about fifty structures, reading the bit positions back out of the compiled program rather than trusting the sizes, and the two compilers agreed on every case except wherelong doublediffers, which is a fact about the member and not about the record. Two of the rules are not what the documents suggest: a bit-field whose alignment has been capped by#pragma packstays where it is rather than moving to the capped boundary, and an unnamed bit-field occupies its bits without giving the record its type's alignment, sostruct { char c; int :20; }is four bytes aligned to one while the same structure with the field named is four bytes aligned to four. -
The integer promotions and the usual arithmetic conversions in
rucc-types, which decide what type an arithmetic expression has. The answers were read out of gcc 13.3 and clang 18 with_Genericnaming the type of every interesting pair rather than derived from the standard, and the standard was then used to explain what was measured. All three of the C23 changes are in:boolis a real type, an enumeration promotes through whatever it is represented in rather than through a type the implementation picked, and_BitIntdoes not promote at all, so_BitInt(8) + _BitInt(8)stays eight bits wide wherechar + charis anint. A_BitIntis ranked against the standard types by its width, so a_BitInt(40)outranks anintand loses to along, and a standard type wins a tie at equal width. Bit-fields are promoted by their width rather than by the type they were declared with, and a bit-field wider than anintkeeps its declared type, which is what both compilers do and what the older wording would have got wrong by eight bits. -
Type compatibility and the composite type in
rucc-types, which is the relation declaration merging is stated in: whether two declarations of one name are talking about the same thing, and what type is left when they are. It is looser than identity on purpose, soint f(int a[3])andint f(int *a)are different types and the same function, an enumeration is compatible with whatever it is represented in, and an array with a size is compatible with one without. The composite is the type that knows both halves, soextern int a[]; int a[4];ends up as an array of four andvoid f(); void f(int);ends up as the prototype, which is what lets the calls written in between be checked against something. The rule for an old style declaration against a prototype is the one gcc states in its own diagnostic, that an argument type with a default promotion cannot match an empty parameter name list, sovoid f(int); void f();merges andvoid f(float); void f();conflicts. Records use the C23 rule that the same tag with the same members is the same type, with a guard so that a self referential structure compared against another declaration of itself terminates instead of going round forever. Two divergences are recorded rather than resolved: clang 18 implements that C23 record rule and gcc 13.3 still rejects the redefinition outright, and clang treats an empty parameter list as a prototype even in C17 mode where gcc keeps the old meaning.
Fixed
- The
__has_*operators answer in ordinary text and not only inside a#if. GCC and clang both implement them as builtin macros, so a header may write#define HAVE_COLD __has_attribute(cold)and then useHAVE_COLDin a declaration, and until now that carried the unexpanded call to the use site instead of the answer. The three whose operand is a header name,__has_include,__has_include_nextand__has_embed, are refused outside a directive rather than answered, because by then the line has been scanned as ordinary tokens and<stdio.h>is a run of comparisons with no header name left in it. Both compilers make that an error too.
What's Changed
- Answer the _has* operators in ordinary text by @tamnd in #49
- Add the interned type universe and layout to rucc-types by @tamnd in #50
- Lay out records, bit-fields included by @tamnd in #51
- Add the integer promotions and the usual arithmetic conversions by @tamnd in #52
- Add type compatibility and the composite type by @tamnd in #53
- Release 0.2.1 by @tamnd in #54
Full Changelog: v0.2.0...v0.2.1