Expand description
The C type system, interned, and layout computation.
Design: spec/07-types-and-semantics.md. Layer rank 2, see spec/18-package-layout.md.
There is one Types per translation unit and it owns every type in it. A TypeId is
four bytes and two of them are equal exactly when they are the same type, which turns the
question the compiler asks more often than any other into an integer comparison.
Two ideas shape the rest of it.
Sugar is kept and never decided on. typedef int32_t; gives a node that remembers the
name and points at canonical int. Every semantic rule reads Types::canonical and sees
int; every diagnostic reads the type as it was written and says int32_t. Compilers that
throw the name away produce messages nobody can act on, and compilers that decide on the
name produce wrong answers, and both are common. Sugar is not only at the outermost node,
so int32_t * and int32_t[4] are sugar too and canonicalising rebuilds them.
_Atomic is a type, not a qualifier. const and volatile and restrict are a
bitmask in the interning key, because nothing about them changes what an object is. C lets
_Atomic be written in the same position, but _Atomic(T) can have a different alignment
from T, so it is a type constructor here and the parser is what maps the spelling onto
it. Document 01 recorded a compiler that treated it as a qualifier and lost track of it,
which is exactly the shortcut that makes atomics silently wrong.
Layout comes out of TargetInfo and never out of the host.
long is four bytes on Windows and eight on Linux, and long double is eight bytes on
Apple and sixteen on SysV x86-64, so a cross compiler that asks its own platform is wrong
twice before it has read a line of C.
use rucc_target::{TargetInfo, Triple};
use rucc_types::{IntKind, Types, layout};
let mut types = Types::new();
let linux = TargetInfo::new("x86_64-unknown-linux-gnu".parse::<Triple>().unwrap());
let windows = TargetInfo::new("x86_64-pc-windows-msvc".parse::<Triple>().unwrap());
let long = types.int(IntKind::Long);
assert_eq!(layout(&types, long, &linux).unwrap().size, 8);
assert_eq!(layout(&types, long, &windows).unwrap().size, 4);Records are laid out by layout_record, which takes the members and gives back their
offsets, and the result is handed to Types::complete_record so that the record then has
a size like any other type. Bit-fields, packed, #pragma pack, aligned, zero width
bit-fields and flexible array members are all in there, and every one of their rules was
measured against gcc and clang rather than read off a document.
promote and usual_arithmetic are 6.3.1.1 and 6.3.1.8, the rules that decide what
type an arithmetic expression has. Their answers were read out of gcc and clang with
_Generic naming the type of every interesting pair, which is also how the C23 changes were
pinned down: _BitInt does not promote, and an enumeration promotes through whatever it is
represented in.
__int128 is one of the integer kinds rather than a _BitInt(128) in disguise. The two are
different types: __int128 is sixteen bytes aligned to sixteen everywhere, _BitInt(128) is
aligned to its granule, and __int128 outranks long long where a _BitInt is ranked by
width alone. It is available on every target here, because all three architectures are
64-bit and GCC has it on every 64-bit target it supports.
compatible and composite are 6.2.7, the relation that decides whether two
declarations of one name are talking about the same thing and the type that is left when they
are. Identity is not that relation: int f(int a[3]) and int f(int *a) are different types
and the same function. The composite is what a caller merging two declarations should keep,
because it is the only one of the three types in play that knows both the array size and the
parameter list.
§Status
The type universe, the interner, the canonical and sugar split, the qualifier rules, layout
with records included, the arithmetic conversions, compatibility with the composite type, and
spell, which writes a type back as the C declaration it is, are implemented.
Not here yet, and named so that the gap is not mistaken for a decision: the decimal floating types.
Every crate in the workspace is published, and publishing implies a promise. This one is
tier 3: its Rust API is explicitly unstable and will change without a major version bump.
Depend on the rucc binary’s behaviour, not on this.
Structs§
- EnumId
- The identity of an
enumdeclaration inTypes. - Enum
Info - What is known about one
enumdeclaration. - Field
- One member of a record, placed.
- Field
Decl - One member of a record as the program wrote it.
- Function
Id - The identity of a function type in
Types. - Function
Type - A function type.
- Integer
Info - What an integer type is once it no longer matters how it was spelled.
- Layout
- The size and alignment of a complete object type.
- Qualifiers
- The qualifiers a type can carry.
- Record
Id - The identity of a
structoruniondeclaration inTypes. - Record
Info - What is known about one
structoruniondeclaration. - Record
Layout - A record, laid out.
- Record
Options - What the program asked for on the record itself.
- Type
- A type with its qualifiers, which together are one entry in the type table.
- TypeId
- The identity of a type.
- Types
- Every type in one translation unit.
- VlaId
- The identity of one variable length array’s size expression.
Enums§
- Array
Len - How many elements an array has, which is four different answers in C.
- Float
Kind - The real floating types.
- IntKind
- The standard integer types, the character types kept apart from them, and
__int128. - Layout
Error - Why a type has no layout.
- Record
Error - Why a record has no layout.
- Record
Kind - Whether a record is a
structor aunion. - Type
Kind - What a type is, with its qualifiers stripped off into
Type::quals.
Constants§
- MILESTONE
- The milestone in
spec/17-milestones.mdthat fills this crate in.
Functions§
- adjust_
parameter - The type a parameter declared as
idreally has, 6.7.6.3p7 and p8. - compatible
- Whether a declaration of
leftand a declaration ofrightdeclare the same thing, 6.2.7. - composite
- The composite type of two compatible types, 6.2.7p3, and
Nonewhen they are not compatible. - declare
- The type as a declaration of
name, which is how it would be written in the program. - element
- The element type of an array or a vector, or
Nonewhere it is neither. - float_
format - The binary format a real floating type has on
target. - float_
width - The width of a real floating type in bits, including the padding
long doublecarries. - int_
width - The width of a standard integer type in bits.
- integer_
info - The signedness and width of an integer type, and
Nonewhenidis not one. - is_
aggregate - An aggregate type, 6.2.5p21: an array or a
struct. - is_
arithmetic - An arithmetic type, 6.2.5p18: the integer types and the floating types.
- is_
array - An array type.
- is_
atomic _Atomic(T), whateverTis.- is_
complete - A complete type: one whose size is known, so an object of it can exist.
- is_
complex - A complex type,
_Complex T. - is_
floating - A floating type, which is the real ones and the complex ones together.
- is_
function - A function type.
- is_
integer - An integer type, 6.2.5p17.
- is_
modifiable - Whether a value of this type may be modified, 6.3.2.1p1.
- is_
object - An object type, 6.2.5p1: anything that is not a function type.
- is_
pointer - A pointer type.
- is_real
- A real type, 6.2.5p17: the integer types and the real floating types.
- is_
real_ floating - A real floating type:
float,double,long doubleand the extended ones. - is_
record - A
structor aunion. - is_
scalar - A scalar type, 6.2.5p21: the arithmetic types and the pointer types.
- is_
vector - A GNU vector type.
- is_void
void.- layout
- The size and alignment of
idontarget. - layout_
record - Lays out a
structor aunion. - pointee
- What a pointer points to, or
Nonewhere it is not a pointer. - promote
- The integer promotions, 6.3.1.1.
- promote_
bit_ field - The integer promotions applied to a bit-field of the given width.
- spell
- The type as a type name, which is how it would be written in a cast or in a
sizeof. - usual_
arithmetic - The usual arithmetic conversions, 6.3.1.8: the one type both operands are converted to.