Fil-C on Nix

Filnix wiki

mbrock/filnix/nanofil

c_types.pl

C's types as terms: specifiers and declarators become type terms, and from those come sizes and layouts, the integer conversions and the values of constant expressions (milestone 4a).

Types

TypeMeans
void boolas in C
char schar ucharchar, signed char, unsigned char
short int long llongthe signed integer types
ushort uint ulong ullongtheir unsigned counterparts
float double ldoublethe floating types
ptrTa pointer to T
arrayT, NN elements of T, or arrayT, none of unknown length
arrayT, vlaEa variable length array whose size expression E is not yet evaluated; E is star for [*]
arrayT, dynVa variable length array whose length lowering evaluated into the IR value V
funcRet, Params, ArityParams a list of types, or unspecified; Arity fixed or variadic
structId unionIdthe struct or union Id of the environment's Defs
badReasona type the frontend cannot handle yet

Parameter types are already adjusted: arrays and functions become pointers. A type with a vla or dyn length somewhere inside is variably modified (vm_type/1); lowering evaluates each vlaE where C evaluates it, which makes it dynV. Qualifiers are dropped, and an enum type is its underlying integer type. A badReason is an error only when a declaration uses it, so headers that mention such types still parse.

The environment

The semantic environment eOrds, Tags, Defs, Next has four parts:

PartFromTo
Ordsan ordinary identifiertypedefT, enumconstV, T, or what lowering stores for a variable or function
Tagsa tagtagKind, Id, Kind struct or union, or enumT
Defsa struct or union Idits definition, below
Nextthe next Id

A definition is one of:

DefinitionMeans
incompleteTagdeclared, not yet defined; Tag is someName, or none when anonymous
defKind, Members, Size, Aligncomplete: Members in order, and the layout
partialReason, Membersonly the members whose offsets are known: no usable layout
badReasona definition the frontend cannot handle yet

and a member one of:

MemberMeans
msomeField, Type, OffsetField, of Type, Offset bytes in
mnone, Type, Offsetan anonymous struct or union member
msomeField, Type, Offset, bitsPos, Widtha bit-field: Width bits from bit Pos of the Type at Offset

Lowering keeps entries of its own in Defs too. A block scope saves Ords and Tags and restores them when it ends; Defs and Next only grow. A predicate that changes the environment takes Env0 and gives Env as its last two arguments, so it threads through foldl/4,6.

modulec_types
useslists
usesassoc
usesieee

Documentation is a relation of the module, doc(Head, Det, Props) (docs/dterm.md); tests/unit.pl runs every example.

94:- discontiguousdoc/3
usesc_parsec_operator/4, compound_assignment/2, c_constant_expression/3

Environment

empty_env-Envdet

Env has no user identifiers or tags, and the builtin va_list record.

103empty_enveOrds, Tags, Defs, 0 ←
empty_assocOrds
empty_assocTags
findallK−CD, , Cs
list_to_assoc'builtin.va_list'−Def|Cs, Defs
complex_type+T, -Fsemidet
Tina type, perhaps qualified
Foutits real type: float, double or ldouble

_Complex F (6.2.5p11) is laid out, copied, passed and returned as a struct of two F members, the real and the imaginary part, whose names no C code can spell: clang compiles _Complex float and double to the code of struct { F re, im; }. It is not an arithmetic type here, since nanofil's arithmetic values sit in registers; its operators and conversions are the cases that name it.

C11 6.2.5p13GCC manual, Complex Numbers
examples 3/3 hold
✓complex_typestruct'builtin.complex.double', double
✓complex_typequalifiedconst, struct'builtin.complex.float', float
✓complex_typedouble, _⟹fails
125complex_typeT0, F ←
unqualifiedT0, structK
atomK
complex_of?F, ?Tnondet
Fin/outfloat, double or ldouble
Tin/out_Complex F, the struct complex_type/2 names
examples 2/2 hold
✓complex_offloat, struct'builtin.complex.float'
✓complex_ofint, _⟹fails
134complex_ofF, structK ←
complex_key
136'builtin.complex.float'float
137'builtin.complex.double'double
138'builtin.complex.ldouble'ldouble
140complex_defF, defstruct, msome'builtin.re', F, 0, msome'builtin.im', F, S, Size, A ←
type_size_alignF, _, S, A
Size is 2 × S
143va_list_defdefstruct, msomegp_offset, uint, 0, msomefp_offset, uint, 4, msomeoverflow_arg_area, ptrvoid, 8, msomereg_save_area, ptrvoid, 16, 24, 8
lookup_ordinary+Env, +Name, ?Valuesemidet

Value is what the ordinary identifier Name stands for in Env.

152lookup_ordinaryeOrds, _, _, _, Name, Value ←
get_assocName, Ords, Value
bind_ordinary+Name, +Value, +Env0, -Envdet

Env is Env0 with the ordinary identifier Name standing for Value, replacing any binding it had.

160bind_ordinaryName, Value, eOrds0, Tags, Defs, Next, eOrds, Tags, Defs, Next ←
put_assocName, Ords0, Value, Ords
env_scope+Env, -Scope, ?Unuseddet

Scope is the scoped part of Env (its ordinary identifiers and tags), for env_restore/3. The third argument is unused.

169env_scopeeOrds, Tags, _, _, sOrds, Tags, _
env_restore+Scope, +Env0, -Envdet

Env is Env0 with its scoped part replaced by Scope: the identifiers and tags of an inner scope are gone, its definitions stay.

176env_restoresOrds, Tags, e_, _, Defs, Next, eOrds, Tags, Defs, Next
sema_error+Loc, +Fmt, +Args

Throw nanofil_error with the message Fmt/Args, prefixed with Loc. Never succeeds.

183sema_errorLoc, Fmt, Args ←
formatatomMsg0, Fmt, Args
formatatomMsg, '~w: ~w', Loc, Msg0
thrownanofil_errorMsg

Declarations

decl_types+Specs, +Inits, -Decls, +Env0, -Envdet

Decls are d(Name, Type, Storage, Asm, Init) for the declarators Inits under the specifiers Specs, Storage as decl_storage/2 gives it. A typedef declares no Decls; Env is Env0 with its names bound instead. Env also has any struct, union or enum Specs defines, and each object's alignment request (object_alignment/5). Throws not_constant when an enumerator's value is not constant.

199decl_typesSpecs0, Inits, Decls, Env0, Env ←
decl_storageSpecs0, Storage
alignment_requestsSpecs0, Requests, Specs
specs_typeSpecs, Base, Env0, Env1
foldldeclarator_typeSpecs, Requests, Base, Storage, Inits, Decls0, Env1, Env
exclude==none, Decls0, Decls
decl_storage+Specs, -Storagedet
Specsina declaration's specifiers
Storageoutnone, typedef, static, extern, auto or register, or thread_local(S) with S one of none, static or extern

Storage is the declaration's storage-class specifier. _Thread_local (also spelled thread_local and GNU __thread) may appear with static or extern, and then gives the object thread storage duration instead of static: one instance per thread, created and initialized when the thread first uses it (Fil-C creates it lazily, as clang's getter does). Where it may appear is the lowering's to check.

examples 5/5 hold
✓decl_storagestoragestatic, tspecint, S⟹S = static
✓decl_storagetspecint, S⟹S = none
✓decl_storagestorage(thread_local), storagestatic, tspecint, S⟹S = thread_localstatic
✓decl_storagestorageextern, storage(thread_local), tspecint, S⟹S = thread_localextern
✓decl_storagestorage(thread_local), tspecint, S⟹S = thread_localnone
225decl_storageSpecs, Storage ←
memberstorageS0, Specs
S0 ≢ (thread_local)
→
S = S0
else
S = none
memberchkstorage(thread_local), Specs
→
Storage = thread_localS
else
Storage = S
declarator_type+Specs, +Requests, +Base, +Storage, +Init, -Decl, +Env0, -Env

Decl is the d/5 term Init declares, or none for a typedef, which binds the name. An object's alignment beyond its type's (object_alignment/5, from the alignment Requests and its typedef's) is bound to qalign(Name), none when it has none and an outer declaration did. A typedef's alignment (typedef_alignment/5) is bound to talign(Name).

239declarator_typeSpecs, Requests0, Base, Storage, initdclsomeName, SyntaxType, Asm, Init, Decl, Env0, Env ←
apply_declaratorSyntaxType, Base, Type0, Env0, Env1
reflect_typetypeSpecs, SyntaxType, Type0, Env1, Reflected
bind_ordinaryqtypeName, Reflected, Env1, Env2
inherited_alignmentSpecs, SyntaxType, Env2, Inherited
Storage ≡ typedef
→
typedef_alignmentRequests0, Inherited, Type0, Env2, TA
TA = badWhy
→
Type1 = badWhy
TA1 = none
Type0 = vecE, N, _
integerTA
→
Type1 = vecE, N, TA
TA1 = TA
else
Type1 = Type0
TA1 = TA
Type1 = vec_, _, _
Type1 ≢ Type0
→
Reflected = qualifiedQs, _
→
qualifyQs, Type1, R1
else
R1 = Type1
bind_ordinaryqtypeName, R1, Env2, Env2a
else
Env2a = Env2
bind_ordinaryName, typedefType1, Env2a, Env3
TA1 ≡ none
¬lookup_ordinaryEnv3, talignName, _
→
Env4 = Env3
else
bind_ordinarytalignName, TA1, Env3, Env3a
integerTA1for expr_alignment/3, unscoped
→
Env3a = eOrds, Tags, Defs0, Next
put_assocaligned_typedefType1, Defs0, TA1, Defs
Env4 = eOrds, Tags, Defs, Next
else
Env4 = Env3a
transparent_typedefSpecs, Type1, Env4, Env
Decl = none
else
Inherited = alignIA
→
Requests = alignasvalueIA|Requests0
else
Requests = Requests0
object_alignmentRequests, Type0, Env2, Type, A
A ≡ none
lookup_ordinaryEnv2, qalignName, _
→
bind_ordinaryqalignName, none, Env2, Env3
A ≡ none
→
Env3 = Env2
else
bind_ordinaryqalignName, A, Env2, Env3
constant_objectName, Reflected, Type0, Storage, Init, Env3, Env
Decl = dName, Type, Storage, Asm, Init
object_alignment+Requests, +Type0, +Env, -Type, -Adet
Requestsinthe _Alignas specifiers and GNU aligned attributes of an object's declaration
Type0inthe declared type
Envinthe scope
TypeoutType0, or bad(Reason) for a request nanofil cannot honor
Aoutthe alignment the object needs beyond its type's, or none

An alignment specifier or aligned attribute on an object declaration aligns that object, not its type: the strictest request, or the type's own alignment if stricter, is the object's. _Alignas(0) and a request no stricter than the type's change nothing; aligned with no argument is 16, x86-64's largest alignment (__BIGGEST_ALIGNMENT__). Lowering honors up to 4096 bytes, the most an alloc or a data item aligns to (above 16 as Fil-C's filc_allocate_with_alignment and its padded data objects do); a stricter request, or one on a function, is refused (bad). The type, and so sizeof and _Alignof of it, is unchanged.

C11 6.7.5p2C11 6.7.5p6GCC manual, Common Variable Attributes, aligned
examples 7/7 hold
✓
object_alignmentattrgroupidaligned, groupint8, none−dec, arraychar, 4, E, T, A
⟹T = arraychar, 4, A = 8
✓
object_alignmentalignasexprnum2, none−dec, int, E, T, A
⟹T = int, A = none
✓
object_alignmentalignastypetypetspeclong, base, char, E, T, A
⟹T = char, A = 8
✓
object_alignmentattrgroupid'__aligned__', char, E, _, A
⟹A = 16
✓
object_alignmentalignasexprnum64, none−dec, int, E, T, A
⟹T = int, A = 64
✓
object_alignmentalignasexprnum8192, none−dec, int, E, T, _
⟹T = bad'an alignment above 4096'
✓
object_alignmentalignasexprnum16, none−dec, bad'vector types', E, T, _
⟹T = bad'vector types'
316object_alignment[], Type, _, Type, none ←
!
317object_alignment_, badR, _, badR, none ←
!

keep the first reason

318object_alignment_, func_, _, _, _, bad'alignment attributes', none ←
!
319object_alignmentRequests, Type0, Env, Type, A ←
innermost_elementType0, Elema VLA has no constant size
catchtype_size_alignElem, Env, _, Natural, _, fail
maplistrequest_alignmentEnv, Requests, As
max_listNatural|As, Max
→
Max =:= Natural
→
Type = Type0
A = none
Max ≤ M
→
Type = Type0
A = Max
else
Type = bad'an alignment above 4096'
A = none
else
Type = bad'alignment attributes'
A = none
constant_object+Name, +Reflected, +Type, +Storage, +Init, +Env0, -Envdet
Nameina declared object
Reflectedinits reflected type, with its qualifiers
Typeinits type
Storageinits storage class
Initinits initializer, or none
Env0inthe scope it is declared in
EnvoutEnv0 with cval(Name) bound to its value c(V) if it is a constant object, else to none if an outer one was

An object of const, not volatile, arithmetic type whose definition has an initializer that folds is a constant in constant expressions where its definition is visible, as clang and GCC fold it (6.6p10 lets an implementation accept other forms): in initializers, case labels, enumerators, static assertions and array sizes, which so declare no variable length array. Any other declaration of the name hides it, except an extern one without an initializer, which declares the same object. A parameter's reflected type has no top-level qualifiers, so a parameter is never read as one.

examples 4/4 hold
✓
constant_object'K', qualifiedconst, int, int, none, num300, none−dec, E0, E
lookup_ordinaryE, cval'K', C
⟹C = c300
✓
constant_object'K', qualifiedconst, char, char, static, num300, none−dec, E0, E
lookup_ordinaryE, cval'K', C
⟹C = c44
✓
constant_object'K', qualifiedconst, (volatile), int, int, none, num7, none−dec, E0, E
¬lookup_ordinaryE, cval'K', _
✓
bind_ordinarycval'K', c7, E0, E1
constant_object'K', int, int, none, none, E1, E
lookup_ordinaryE, cval'K', C
⟹C = none
362constant_objectName, Reflected, Type, Storage, Init, Env0, Env ←
Reflected = qualifiedQs, _
memberchkconst, Qs
¬memberchk(volatile), Qs
Init ≢ none
Init ≠ list_
catchconstant_valueInit, Env0, V0, From, not_constant, fail
const_convertV0, From, Type, V
integerV
→
bind_ordinarycvalName, cV, Env0, Env
Storage ≡ extern
Init ≡ none
→
Env = Env0
lookup_ordinaryEnv0, cvalName, _
→
bind_ordinarycvalName, none, Env0, Env
else
Env = Env0
max_alignment-A

the strictest alignment nanofil gives an object.

376max_alignment4096
typedef_alignment+Requests, +Inherited, +Type, +Env, -Adet
Requestsinthe aligned attributes of a typedef declaration
Inheritedin[align(I)] if it names a typedef of alignment I (inherited_alignment/4), else []
Typeinthe type it declares
Envinthe scope
Aoutthe typedef's alignment, none for its type's own, or bad(Reason)

GNU aligned on a typedef sets the alignment of the typedef, not of its type, and may lower it as well as raise it; sizeof is unchanged (clang gives typedef struct { long a[3]; } B __attribute__((aligned)) size 24, alignment 16). Without one, a typedef of a typedef keeps its alignment. _Alignas may not be applied to a typedef, and an alignment above 4096 or one that is not a constant power of two is refused.

GCC manual, Common Type Attributes, alignedC11 6.7.5p2
examples 5/5 hold
✓
typedef_alignmentattrgroupid'__aligned__', [], long, E, A
⟹A = 16
✓
typedef_alignmentattrgroupidaligned, groupint1, none−dec, [], int, E, A
⟹A = 1
✓
typedef_alignment[], align64, int, E, A
⟹A = 64
✓
typedef_alignment[], [], int, E, A
⟹A = none
✓
typedef_alignmentattrgroupidaligned, groupint8192, none−dec, [], int, E, A
⟹A = bad'an alignment above 4096'
407typedef_alignment[], Inherited, _, _, A ←
!
Inherited = alignA
→
true
else
A = none
409typedef_alignmentRequests, _, _, _, bad'_Alignas on a typedef' ←
memberchkalignas_, Requests
!
411typedef_alignmentRequests, _, _, Env, A ←
maplistrequest_alignmentEnv, Requests, As
→
max_listAs, A0
→
A = bad'an alignment above 4096'
else
A = A0
else
A = bad'alignment attributes'
inherited_alignment+Specs, +SyntaxType, +Env, -Inheriteddet
Specsina declaration's specifiers
SyntaxTypeinits declarator's type syntax
Envinthe scope
Inheritedout[align(A)] if Specs name a typedef of alignment A (typedef_alignment/5) and the declarator declares an object or array of it, else []
GCC manual, Common Type Attributes, aligned
examples 2/2 hold
✓
c_types:bind_ordinarytalign'B', 64, E0, E
inherited_alignmenttname'B', arraybase, none, E, I
⟹I = align64
✓
c_types:bind_ordinarytalign'B', 64, E0, E
inherited_alignmenttname'B', ptrbase, [], E, I
⟹I = []
433inherited_alignmentSpecs, SyntaxType, Env, Inherited ←
memberchktnameN, Specs
lookup_ordinaryEnv, talignN, A
integerA
→
Inherited = alignA
else
Inherited = []
440object_declaratorbase
441object_declaratorarrayS, _ ←
443innermost_elementarrayT, _, E ←
444innermost_elementT, T
request_alignment+Env, +Request, -A

the constant alignment Request asks for, a power of two or 0 (none).

448request_alignment_, alignasvalueA, A ←
!
449request_alignmentEnv, alignastypeTN, A ←
!
TN = typeSpecs, Syntax
inherited_alignmentSpecs, Syntax, Env, alignIA
→
A = IA
else
type_name_typeTN, T, Env, Env1
type_size_alignT, Env1, _, A
455request_alignmentEnv, alignasexprE, A ←
!
catchconst_valueE, Env, A, _, not_constant, fail
A =:= 0
→
true
else
A > 0
A /\ A−1 =:= 0
458request_alignmentEnv, attrgroupAttr, A ←
!__attribute__((...))
request_alignmentEnv, attrAttr, A
460request_alignment_, attrWord, 16 ←
memberchkWord, idaligned, id'__aligned__'
!
462request_alignmentEnv, attrWord, groupTs, A ←
memberchkWord, idaligned, id'__aligned__'
phraseflat_tokensTs, Flat
catchconst_valueE, Env, A, _, not_constant, fail
A > 0
A /\ A−1 =:= 0
reflect_type+TypeName, +Type, +Env, -Reflecteddet
TypeNameina type name, type(Specs, Syntax)
Typeinthe unqualified type code generation gives it
Envinthe scope
ReflectedoutType with its qualifiers kept, qualified(Qs, T) at each level that has any, and enum(K) for an enum

Reflection is how the frontend knows const, volatile and _Atomic without putting qualifiers into the types code generation sees.

Keep qualification for type reflection without changing the unqualified code-generation types. These entries share ordinary identifier scope, including shadowing and typedefs. Lvalue conversion removes only the outer qualification, never the qualification of a pointer's target. reflect_declarator/5 is deterministic; a choicepoint left by each declaration would keep its terms alive to the end of the file.

484reflect_typetypeSpecs, Syntax, Type, Env, Reflected ←
oncereflect_declaratorSyntax, Specs, Type, Env, Reflected
enum_key+Tag, +Enumerators, -K

an enum type's identity in reflected types, enum(K) among its qualifiers (composite_type/3): its tag, or for an anonymous one its first constant, which no other enum in scope can declare.

enum_key
491someName_Name
492noneenumeratorFirst, _|_anonFirst
493nonepackedenumeratorFirst, _|_anonFirst
495reflect_declaratorbase, Specs, Type, Env, Reflected ←
memberchktnameName, Specs
lookup_ordinaryEnv, qtypeName, T
→
Base0 = T
memberchktypeofexprE, Specs
→
Base0 = T
memberchktypeoftypeTN, Specs
type_name_typeTN, T0, Env, _
→
reflect_typeTN, T0, Env, Base0
memberchkatomicTN, Specs
type_name_typeTN, T0, Env, _
→
reflect_typeTN, T0, Env, B0
qualifyatomic, B0, Base0
memberenumTag, Enumerators, Specs
enum_keyTag, Enumerators, K
→
Base0 = qualifiedenumK, Type
else
Base0 = Type
atomic_su_baseType, Base0, Base
findallQ, memberqualQ, Specs, Qs
qualifyQs, Base, Reflected
511reflect_declaratorptrS, Qs, Specs, ptrT, Env, R ←
reflect_declaratorS, Specs, T, Env, RT
qualifyQs, ptrRT, R
513reflect_declaratorarrayS, _, Specs, arrayT, N, Env, arrayRT, N ←
reflect_declaratorS, Specs, T, Env, RT
515reflect_declaratorfuncS, Params, Specs, funcT, Ps, V, Env, funcRT, RPs, V ←
reflect_declaratorS, Specs, T, Env, RT
reflect_param_typesParams, Ps, Env, RPs
518reflect_declarator_, _, badWhy, _, badWhy
atomic_su_base+Type, +Base0, -Base

when the specifiers name an _Atomic struct or union, Type = atomic(S, P) (atomic_su_type/3), in any spelling (_Atomic(T), or _Atomic on a tag, typedef or typeof name), the reflected Base keeps it under Base0's qualifiers.

524atomic_su_baseatomic_, P, Base0, Base ←
!
Base0 = qualifiedQs, B
→
true
else
Qs = []
B = Base0
B = atomic_, _
→
Base = Base0
Qs ≡ []
→
Base = atomicB, P
else
Base = qualifiedQs, atomicB, P
530atomic_su_base_, Base, Base
532reflect_param_typesunspecified, unspecified, _, unspecified
533reflect_param_typesparamsParams, _, Ts, Env, RTs ←
maplistreflect_param_typeEnv, Params, Ts, RTs
536reflect_param_typeEnv, paramSpecs, dcl_, Syntax, _, T, RT ←
parameter_syntaxSyntax, Adjusted
reflect_typetypeSpecs, Adjusted, T, Env, R0
541parameter_syntaxarrayS, _, ptrS, [] ←
!
542parameter_syntaxfuncS, P, ptrfuncS, P, [] ←
!
543parameter_syntaxS, S
545qualifyQs0, qualifiedQs1, T, R ←
!
appendQs0, Qs1, Qs
qualifyQs, T, R
547qualifyQs, arrayT0, N, arrayT, N ←
Qs ≢ []C11 6.7.3p9
!
qualifyQs, T0, T
549qualifyQs0, T, R ←
sortQs0, Qs
Qs ≡ []
→
R = T
else
R = qualifiedQs, T
reflect_params+Declarator, +Types, +Env0, -Envdet
Declaratorina function definition's declarator
Typesinits parameters' types
Env0inthe function's scope
EnvoutEnv0 with each named parameter's reflected type (reflect_type/4)

A declarator that is not a parameter list leaves Env0 as it is.

559reflect_paramsfunc_, paramsParams, _, Types, Env0, Env ←
!
foldlreflect_param, Params, Types, Env0, Env
561reflect_params_, _, Env, Env
563reflect_paramParam, T, Env0, Env ←
Param = param_, dclsomeName, _, _
!
reflect_param_typeEnv0, Param, T, RT
bind_ordinaryqtypeName, RT, Env0, Env
567reflect_param_, _, Env, Env

Specifiers

specs_type+Specs, -Type, +Env0, -Envdet

Type is the type the declaration specifiers Specs name, bad(Reason) if the frontend cannot handle it, as for an address_space attribute. Env is Env0 with the struct, union and enum definitions in Specs entered.

578specs_typeSpecs, Type, Env0, Env ←
base_typeSpecs, Type0, Env0, Env
memberattrTokens, Specs
attr_wordTokens, W
memberchkW, address_space, '__address_space__'
→
Type = badaddress_space
memberattrTokens, Specs
→
vector_typeSpecs, Type0, Env, Type
memberchkqualatomic, Specs
atomic_su_typeType0, Env, T
→
Type = T
memberchkqualatomic, Specs
Type0 ≠ bad_
→
Type = bad'_Atomic on a type other than an integer, pointer, float or double'
memberattrTokens, Specs
mode_attrTokens, Mode
→
mode_typeType0, Mode, Type
memberSpec, Specs
→
Type = bad'alignment attributes'
else
Type = Type0
known_attribute-Word, -Kind, -Whynondet
Wordouta GNU attribute's name, without GCC's optional underscores
Kindouthint, an attribute that changes nothing nanofil emits, or honored, one nanofil implements
Whyoutwhy: what it does, and why nanofil may ignore it or where it is honored

nanofil accepts an attribute only if it is listed. One that changes layout, lifetime, linkage, calling or values must be honored (and its unsupported uses refused there), never dropped: cleanup and mode were once ignored and miscompiled. c_parse refuses any other attribute, "attribute X is not supported", where it reads it; ifunc, weakref, naked, ms_abi, scalar_storage_order and address_space are among those.

GCC manual, Attribute Syntax and Common Function, Variable and Type AttributesClang documentation, Attributes in Clang
examples 4/4 hold
✓known_attributenonnull, hint, _
✓known_attributecleanup, honored, _
✓known_attributesection, honored, _
✓known_attributeifunc, _, _⟹fails
614known_attributeWord, Kind, Why ←
attribute_entryWord, Kind, Why
616attribute_entryaligned, honored, 'c_types\' layout (object_alignment/5, member alignment)'
617attribute_entrypacked, honored, 'c_types\' layout'
618attribute_entrynanofil_pack, honored, 'c_pp\'s mark of a struct or union defined under #pragma pack(N): su_layout/5 caps its members\' alignment at N'
619attribute_entrytransparent_union, honored, 'transparent_union/3: a parameter of the union takes any member'
620attribute_entrycleanup, honored, 'c_lower cleanup_decls//4 runs it on every scope exit'
621attribute_entrymode, honored, 'mode_type/3'
622attribute_entryvector_size, honored, 'the type is refused (bad) where it is used'
623attribute_entryext_vector_type, honored, 'as vector_size'
624attribute_entryaddress_space, honored, 'the type is refused (bad) where it is used: Fil-C code has one address space, and filcc cannot compile an access through another (FilPizlonator asserts); glibc\'s rseq-internal.h casts to address_space(257) in a static inline function its units never call'
625attribute_entrysection, honored, 'as filcc: FilPizlonator gives neither pizlonatedDO_X nor a function a section, so the object stays in .data, .bss or .rodata and the function in .text, where nanofil puts them; Fil-C code cannot name __start_X or __stop_X (docs/abi-findings.md, *Sections*)'
626attribute_entryweak, honored, 'c_lower weak symbols (weak_attr/1)'
627attribute_entryalias, honored, 'c_lower alias_decl/8; a variable alias is refused'
628attribute_entryweakref, honored, 'c_lower weakref_item/3: an extern reference to its target'
629attribute_entryconstructor, honored, 'c_lower init_decl//5'
630attribute_entrydestructor, honored, 'c_lower init_decl//5'
631attribute_entryalways_inline, hint, 'inlining is the optimizer\'s choice; the call means the same, except that calls of an inline body that is not the external definition use a local copy, as clang inlines them all (c_lower always_inline/1)'
632attribute_entrynoinline, hint, 'as always_inline'
633attribute_entryflatten, hint, 'as always_inline'
634attribute_entryartificial, hint, 'debug information only'
635attribute_entrynodebug, hint, 'debug information only'
636attribute_entrygnu_inline, honored, 'c_lower external_inline_body/4 gives the body GNU\'s extern inline rule'
637attribute_entryhot, hint, 'placement and optimization hint'
638attribute_entrycold, hint, 'placement and optimization hint'
639attribute_entryoptimize, hint, 'per-function optimization level'
640attribute_entryoptnone, hint, 'clang\'s per-function -O0, as optimize; ghostscript\'s ttinterp.c and sjpege.c'
641attribute_entrytarget, hint, 'instruction set of the function; nanofil emits baseline x86-64'
642attribute_entrymin_vector_width, hint, 'vector width hint of clang\'s intrinsic headers'
643attribute_entrynoreturn, hint, 'a promise about calls; nanofil does not rely on it'
644attribute_entryreturns_twice, hint, 'glibc marks setjmp, sigsetjmp and vfork; nanofil lowers setjmp\'s family as intrinsics by name'
645attribute_entryleaf, hint, 'an optimizer promise about calls back into the unit'
646attribute_entrynothrow, hint, 'no C++ exceptions in C'
647attribute_entrypure, hint, 'an optimizer promise; nanofil does not rely on it'
648attribute_entryconst, hint, 'an optimizer promise; nanofil does not rely on it'
649attribute_entrymalloc, hint, 'an aliasing promise about the result; nanofil does not rely on it'
650attribute_entryalloc_size, hint, 'object size hint; nanofil\'s __builtin_object_size answers unknown'
651attribute_entryalloc_align, hint, 'alignment hint about the result'
652attribute_entryalign_value, hint, 'alignment assumption about a pointer parameter (clang\'s intrinsic headers)'
653attribute_entryassume_aligned, hint, 'alignment hint about the result'
654attribute_entryreturns_nonnull, hint, 'a promise about the result'
655attribute_entrynonnull, hint, 'a promise about arguments, for diagnostics and the optimizer'
656attribute_entrynoescape, hint, 'a promise that a pointer argument does not escape; nanofil does not rely on it'
657attribute_entrynonnull_if_nonzero, hint, 'as nonnull'
658attribute_entryformat, hint, 'printf and scanf format checking'
659attribute_entryformat_arg, hint, 'format checking'
660attribute_entrysentinel, hint, 'argument checking'
661attribute_entryaccess, hint, 'argument access checking'
662attribute_entryfd_arg, hint, 'file descriptor checking'
663attribute_entryfd_arg_read, hint, 'file descriptor checking'
664attribute_entryfd_arg_write, hint, 'file descriptor checking'
665attribute_entrynull_terminated_string_arg, hint, 'argument checking'
666attribute_entrytainted_args, hint, 'static analysis only'
667attribute_entrywarn_unused_result, hint, 'a diagnostic'
668attribute_entrynodiscard, hint, 'a diagnostic'
669attribute_entrydeprecated, hint, 'a diagnostic'
670attribute_entryunavailable, hint, 'a diagnostic; a use is an error in clang too'
671attribute_entryerror, hint, 'a diagnostic at calls clang cannot remove'
672attribute_entrywarning, hint, 'a diagnostic'
673attribute_entrydiagnose_if, hint, 'a diagnostic (glibc\'s clang fortify)'
674attribute_entrydiagnose_as_builtin, hint, 'a diagnostic (glibc\'s clang fortify)'
675attribute_entryenable_if, hint, 'only on glibc\'s clang fortify overloads, which nanofil handles as overloadable'
676attribute_entryoverloadable, honored, 'an incompatible redeclaration is skipped (docs/m4.md)'
677attribute_entrypass_object_size, hint, 'glibc\'s clang fortify wrappers; nanofil\'s object sizes are unknown, so they call the plain function'
678attribute_entrypass_dynamic_object_size, hint, 'as pass_object_size'
679attribute_entryunused, hint, 'a diagnostic'
680attribute_entrymaybe_unused, hint, 'a diagnostic'
681attribute_entryused, hint, 'keeps an unreferenced definition for asm to name; nanofil emits every definition but a static inline one, and the only top-level asm it accepts, .symver, versions symbols of external linkage'
682attribute_entryretain, hint, 'as used, for the linker'
683attribute_entryexternally_visible, hint, 'whole-program optimization hint'
684attribute_entryvisibility, hint, 'dynamic export; nanofil exports every external symbol, observable only through interposition'
685attribute_entrynoipa, hint, 'optimizer hint'
686attribute_entrynoclone, hint, 'optimizer hint'
687attribute_entryno_icf, hint, 'optimizer hint'
688attribute_entryno_reorder, hint, 'optimizer hint'
689attribute_entryno_instrument_function, hint, 'profiling instrumentation'
690attribute_entryno_profile_instrument_function, hint, 'profiling instrumentation'
691attribute_entryno_sanitize, hint, 'sanitizer instrumentation'
692attribute_entryno_sanitize_address, hint, 'sanitizer instrumentation'
693attribute_entryno_sanitize_thread, hint, 'sanitizer instrumentation'
694attribute_entryno_sanitize_undefined, hint, 'sanitizer instrumentation'
695attribute_entryno_address_safety_analysis, hint, 'sanitizer instrumentation'
696attribute_entryno_stack_protector, hint, 'stack protector instrumentation'
697attribute_entrystack_protect, hint, 'stack protector instrumentation'
698attribute_entryno_split_stack, hint, 'stack splitting'
699attribute_entryzero_call_used_regs, hint, 'register clearing on return'
700attribute_entrynocf_check, hint, 'control-flow protection marking'
701attribute_entrycf_check, hint, 'control-flow protection marking'
702attribute_entryindirect_return, hint, 'control-flow protection marking'
703attribute_entrypatchable_function_entry, hint, 'padding for live patching'
704attribute_entrynoplt, hint, 'call sequence through the GOT'
705attribute_entrytls_model, hint, 'thread-local access sequence'
706attribute_entryuninitialized, hint, 'opts out of -ftrivial-auto-var-init'
707attribute_entrycommon, honored, 'c_lower weak_name/3: a tentative definition with it is weak, as filcc emits it'
708attribute_entrynocommon, hint, 'nanofil, as clang, never emits common symbols'
709attribute_entrysysv_abi, hint, 'the x86-64 calling convention nanofil uses'
710attribute_entrystdcall, hint, 'an i386 calling convention; clang ignores it on x86-64'
711attribute_entrycdecl, hint, 'an i386 calling convention; clang ignores it on x86-64'
712attribute_entryfastcall, hint, 'an i386 calling convention; clang ignores it on x86-64'
713attribute_entrythiscall, hint, 'an i386 calling convention; clang ignores it on x86-64'
714attribute_entrycapability, hint, 'clang thread safety analysis, a static check only'
715attribute_entryshared_capability, hint, 'clang thread safety analysis, a static check only'
716attribute_entrylockable, hint, 'clang thread safety analysis, a static check only'
717attribute_entryscoped_lockable, hint, 'clang thread safety analysis, a static check only'
718attribute_entryguarded_by, hint, 'clang thread safety analysis, a static check only'
719attribute_entrypt_guarded_by, hint, 'clang thread safety analysis, a static check only'
720attribute_entryguarded_var, hint, 'clang thread safety analysis, a static check only'
721attribute_entrypt_guarded_var, hint, 'clang thread safety analysis, a static check only'
722attribute_entryacquired_before, hint, 'clang thread safety analysis, a static check only'
723attribute_entryacquired_after, hint, 'clang thread safety analysis, a static check only'
724attribute_entryrequires_capability, hint, 'clang thread safety analysis, a static check only'
725attribute_entryrequires_shared_capability, hint, 'clang thread safety analysis, a static check only'
726attribute_entryexclusive_locks_required, hint, 'clang thread safety analysis, a static check only'
727attribute_entryshared_locks_required, hint, 'clang thread safety analysis, a static check only'
728attribute_entryacquire_capability, hint, 'clang thread safety analysis, a static check only'
729attribute_entryacquire_shared_capability, hint, 'clang thread safety analysis, a static check only'
730attribute_entryexclusive_lock_function, hint, 'clang thread safety analysis, a static check only'
731attribute_entryshared_lock_function, hint, 'clang thread safety analysis, a static check only'
732attribute_entryrelease_capability, hint, 'clang thread safety analysis, a static check only'
733attribute_entryrelease_shared_capability, hint, 'clang thread safety analysis, a static check only'
734attribute_entryrelease_generic_capability, hint, 'clang thread safety analysis, a static check only'
735attribute_entryunlock_function, hint, 'clang thread safety analysis, a static check only'
736attribute_entrytry_acquire_capability, hint, 'clang thread safety analysis, a static check only'
737attribute_entrytry_acquire_shared_capability, hint, 'clang thread safety analysis, a static check only'
738attribute_entryexclusive_trylock_function, hint, 'clang thread safety analysis, a static check only'
739attribute_entryshared_trylock_function, hint, 'clang thread safety analysis, a static check only'
740attribute_entryassert_capability, hint, 'clang thread safety analysis, a static check only'
741attribute_entryassert_shared_capability, hint, 'clang thread safety analysis, a static check only'
742attribute_entryassert_exclusive_lock, hint, 'clang thread safety analysis, a static check only'
743attribute_entryassert_shared_lock, hint, 'clang thread safety analysis, a static check only'
744attribute_entrylocks_excluded, hint, 'clang thread safety analysis, a static check only'
745attribute_entrylock_returned, hint, 'clang thread safety analysis, a static check only'
746attribute_entryno_thread_safety_analysis, hint, 'clang thread safety analysis, a static check only'
747attribute_entryforce_align_arg_pointer, hint, 'realigns the stack on entry; Fil-C code is always entered with the ABI\'s 16-byte alignment'
748attribute_entryanalyzer_noreturn, hint, 'clang static analyzer only'
749attribute_entryregparm, hint, 'i386 only; clang ignores it on x86-64'
750attribute_entrysimd, hint, 'declares vector variants of a math function; calls stay scalar'
751attribute_entrynonstring, hint, 'a diagnostic'
752attribute_entrycounted_by, hint, 'bounds hint for clang\'s sanitizers and object sizes; Fil-C checks every access'
753attribute_entrystrict_flex_array, hint, 'object size hint'
754attribute_entrydesignated_init, hint, 'a diagnostic'
755attribute_entryflag_enum, hint, 'a diagnostic'
756attribute_entryenum_extensibility, hint, 'a diagnostic'
757attribute_entrywarn_if_not_aligned, hint, 'a diagnostic'
758attribute_entrycopy, hint, 'copies another declaration\'s attributes, which are checked there'
759attribute_entryfallthrough, hint, 'a diagnostic, as a statement attribute'
760attribute_entrylikely, hint, 'a branch hint, as a statement attribute'
761attribute_entryunlikely, hint, 'a branch hint, as a statement attribute'
762attribute_entryassume, hint, 'a statement attribute whose expression is not evaluated'
763attribute_entrymusttail, hint, 'a statement attribute; the call means the same'
764attribute_entrynomerge, hint, 'optimizer hint'
765attribute_entryreproducible, hint, 'C23 optimizer promise'
766attribute_entryunsequenced, hint, 'C23 optimizer promise'
767attribute_entrymay_alias, hint, 'nanofil does no type-based alias analysis, so it means nothing in any position'
768attribute_entrybitwise, hint, 'sparse\'s annotation, only under __CHECKER__'
filcc_refused_mnemonic-Mnondet
Moutan x86-64 mnemonic, lowercase, as an asm line's first word

filcc's safe inline asm validator refuses M whatever its operands: parseMnemonic fails, and validateSafeInlineAsm's Reason is "unsupported mnemonic for safe inline asm: M". The list is what the pinned filcc answers for __asm__ volatile("M"), probed over the privileged, control-transfer, I/O, prefix and directive mnemonics packages use; a mnemonic not listed may pass or fail for another reason (ud2, pause and the fences pass; cpuid, rdtsc and xgetbv need their register operands declared), so nanofil refuses its asm at compile time rather than guess (c_lower asm_runtime_reason/6).

The port I/O forms with a size suffix are refused too (kbd's resizecons, glibc's sys/io.h, util-linux's lscpu-virt).

filc-staging-ref-src, llvm/lib/Transforms/Instrumentation/FilPizlonator.cpp, validateSafeInlineAsm (parseMnemonic) and handleInlineAsm
filcc_refused_mnemonic'.byte'callclflushclicltsemmsenterfxsavehlticebpininbinlinsbinslinswintint1int3intoinwinvdiretiretqjmpleavelgdtlidtlmswlockmonitormwaitoutoutboutloutsboutsloutswoutwpoppopfpushpushfrdmsrrdpmcrdseedrepretsgdtsidtstdstiswapgssyscallsysentersysexitsysretud1wbinvdwrmsrxsave
mode_attr+Tokens, -Mode

the attribute is GCC's mode(Mode), its name without GCC's optional underscores.

855mode_attrTokens, Mode ←
attr_entryTokens, Word, groupidM0|_
memberchkWord, mode, '__mode__'
!
atom_concat'__', M1, M0
atom_concatMode, '__', M1
→
true
else
Mode = M0
mode_type+Type0, +Mode, -Typedet
Type0inthe type the specifiers name
Modeina machine mode, as mode_attr/2 reads it
Typeoutthe type of that mode, bad(Reason) if nanofil has none

GCC's mode attribute gives an integer or floating type the width of a machine mode, keeping its signedness: QImode is one byte, HImode two, SImode four and DImode eight; byte is QI, and word, unwind_word (libgcc's unwind.h declares _Unwind_Word so) and pointer are DI on x86-64; SF is float and DF double. As clang does, nanofil picks the first standard type of that width and signedness (long, not long long, for DI). gmp's longlong.h declares UQItype, USItype and UDItype so. TImode needs __int128, which nanofil refuses. Ignoring the attribute would keep int's four bytes.

GCC manual, Common Variable Attributes, mode
examples 6/6 hold
✓mode_typeint, 'DI', long
✓mode_typeuint, 'QI', uchar
✓mode_typeint, word, long
✓mode_typeuint, unwind_word, ulong
✓mode_typedouble, 'SF', float
✓mode_typeint, 'TI', bad'mode TI (__int128)'
881mode_typebadWhy, _, badWhy ←
!
882mode_typequalifiedQs, T0, Mode, Type ←
!
mode_typeT0, Mode, T
T = bad_
→
Type = T
else
Type = qualifiedQs, T
885mode_typeType0, Mode, Type ←
integer_typeType0, _, Sign, _
Type0 ≢ bool
mode_bytesMode, Bytes
→
integer_typeType, Bytes, Sign, _
Type ≢ bool
Type ≢ char
→
true
else
Type = bad'mode TI (__int128)'
memberchkType0, float, double
float_modeMode, Type1
→
Type = Type1
else
formatatomWhy, 'mode ~w on this type', Mode
Type = badWhy
mode_bytes
896'QI'1
897byte1
898'HI'2
899'SI'4
900'DI'8
901word8
902unwind_word8
903pointer8
904'TI'16
906float_mode'SF', float
907float_mode'DF', double
vector_attr+Tokenssemidet
Tokensinthe tokens of a GNU attribute

The attribute is vector_size, which makes the declared type a vector of its specifiers' type (immintrin.h's __m128i). nanofil has no vector types: the type is refused (bad), an error only where it is used, so a header declaring vector typedefs and inline functions it never calls still compiles. Ignoring the attribute would give the vector the size of one element.

GCC manual, Using Vector Instructions through Built-in Functions
examples 2/2 hold
✓vector_attrgroupid'__vector_size__', groupint16, none−dec
✓vector_attrgroupidaligned, groupint16, none−dec⟹fails
921vector_attrTokens ←
is_listTokens
attr_wordTokens, Word
memberchkWord, vector_size, '__vector_size__', ext_vector_type
!
vector_type+Specs, +Elem, +Env, -Typedet
Specsindeclaration specifiers with a vector_size attribute
Eleminthe type the other specifiers name
Envinthe scope
Typeoutvec(Elem, N, Align), or bad(Why)

GCC's vector_size(Bytes) makes a vector of Bytes / sizeof(Elem) elements of an integer type other than _Bool, float or double. Its alignment is its size unless an aligned attribute in the same specifiers sets it, which may lower it. A typedef's aligned attribute (typedef_alignment/5) becomes its vector type's alignment, since a vector access is checked against the alignment of the type it is made through (immintrin.h's __m128i_u is aligned(1), so _mm_loadu_si128 checks alignment 1). Only 8-, 16- and 32-byte vectors are supported (the SSE through AVX2 intrinsics, and xmmintrin.h's 8-byte loads and stores); each access is checked as one access of its full width (docs/vector-access.md). They are on unless NANOFIL_VECTORS=0 (--no-vectors), which refuses every vector type.

GCC manual, Using Vector Instructions through Built-in FunctionsClang Language Extensions, Vectors and Extended Vectors
946vector_typeSpecs, Elem0, Env, Type ←
unqualifiedElem0, Elem
getenv'NANOFIL_VECTORS', '0'
→
Type = bad'vector types'
Elem = badWhy
→
Type = badWhy
¬vector_size_valueSpecs, Env, _
→
Type = bad'vector types'ext_vector_type
→
Type = bad'a vector of a type other than an integer, float or double'
else
vector_size_valueSpecs, Env, Size
type_size_alignElem, Env, ES, _
¬memberchkSize, 8, 16, 32
→
formatatomWhy, 'vector types of ~w bytes', Size
Type = badWhy
else
N is Size//ES
memberattrTokens, Specs
attr_entryTokens, AW, Arg
memberchkAW, aligned, '__aligned__'
request_alignmentEnv, attridaligned|Arg, A0
→
A = A0
else
A = Size
Type = vecElem, N, A
972vector_size_valueSpecs, Env, Size ←
memberattrTokens, Specs
attr_entryTokens, W, groupTs
memberchkW, vector_size, '__vector_size__'
phraseflat_tokensTs, Flat
catchconst_valueE, Env, Size, _, not_constant, fail
!
979vector_elementT ←
T ≢ bool
980vector_elementfloat
981vector_elementdouble
layout_attr+Spec

Spec is an attribute that changes layout, which the frontend does not model. The cut commits to the first such word.

985layout_attralignas_
986layout_attrattrTokens ←
is_listTokens
attr_wordTokens, Word
memberchkWord, aligned, '__aligned__', packed, '__packed__'
!
packed_attr+Tokens

, aligned_attr(+Tokens): the attribute tokens name packed, or aligned.

994packed_attrTokens ←
is_listTokens
attr_wordTokens, Word
memberchkWord, packed, '__packed__'
!
997aligned_attrTokens ←
is_listTokens
attr_wordTokens, Word
memberchkWord, aligned, '__aligned__'
!
attr_entry+Tokens, -Word, -Restnondet
Tokensinan __attribute__((...))'s tokens, [group(Items)], or its Items
Wordoutthe name of one of its attributes, as written
Restoutthe tokens after that name in its comma-separated entry

Only the entries' names count, never an identifier among their arguments: in __attribute__((cleanup(weak))) weak names a function, not the weak attribute. Consumers look attributes up through this.

GCC manual, Attribute Syntax
examples 3/3 hold
✓attr_entrygroupidweak, p(','), idalias, groupstr116, alias, R⟹R = groupstr116
✓attr_entryid'__aligned__', groupint8, none−dec, W, _⟹W = '__aligned__'
✓attr_entrygroupidcleanup, groupidweak, weak, _⟹fails
1014attr_entryTokens, Word, Rest ←
is_listTokens
Tokens = groupInner
→
true
else
Inner = Tokens
attr_itemsInner, Items
memberT|Rest, Items
T = idWord
or
T = kwWord
attr_word+Tokens, -Wordnondet
Tokensinthe tokens of a GNU __attribute__((...)) list
Wordoutthe name of one of its attributes (attr_entry/3)
examples 1/1 hold
✓findallW, attr_wordidaligned, p(','), idweak, W, Ws⟹Ws ≡ aligned, weak
1026attr_wordTokens, Word ←
attr_entryTokens, Word, _
base_type+Specs, -Type, +Env0, -Env

Type from the type specifiers alone: either keywords or one named type, never both.

1030base_typeSpecs, Type, Env0, Env ←
findallK, membertspecK, Specs, Keywords
findallS,
memberS, Specs
,
Others
Others = Other
Keywords ≡ []
→
named_typeOther, Type, Env0, Env
Others ≡ []
→
keyword_typeKeywords, Type
Env = Env0
else
Type = bad'conflicting type specifiers'
Env = Env0
other_type_spectname_su_, _, _, _enum_, _typeof_atomic_auto_type

Clang retains the SysV va_list array/record layout; Fil-C stores its capability-bearing cursor in the first word and ignores the rest.

1051named_typetname'__builtin_va_list', arraystruct'builtin.va_list', 1, Env, Env ←
!

clang predefines __int128_t and __uint128_t as __int128 and unsigned __int128, which nanofil refuses where they are used (keyword_combo/2): glibc's bits/link.h pads its audit structs with them.

1056named_typetname'__int128_t', bad'__int128', Env, Env ←
!
1057named_typetname'__uint128_t', bad'__int128', Env, Env ←
!
1058named_typetnameName, Type, Env, Env ←
lookup_ordinaryEnv, Name, typedefType0
→
Type = Type0
else
Type = bad'unknown typedef'

c_parse resolves a declaration's __auto_type from its initializer; one left over (a parameter, member or type name) has none.

1064named_typeauto_type, bad'__auto_type without an initializer', Env, Env
1065named_typetypeofArg, Type, Env0, Env ←
typeof_typeArg, Type, Env0, Env
1067named_typeatomicTN, Type, Env0, Env ←
type_name_typeTN, Type0, Env0, Env
reflect_typeTN, Type0, Env, Reflected
Type0 = bad_
→
Type = Type0
Reflected = qualifiedQs, _6.7.2.4p3
memberQ, Qsenum(K): identity
Q ≠ enum_
→
Type = bad'_Atomic of a qualified type'
→
Type = Type0
atomic_su_typeType0, Env, T
→
Type = T
else
Type = bad'_Atomic of a type other than an integer, pointer, float or double'
1079named_typesuKind, Tag, Members, Attrs, Type, Env0, Env ←
su_typeMembers, Kind, Tag, Attrs, Type, Env0, Env
1081named_typeenumTag, Enumerators, Type, Env0, Env ←
enum_typeEnumerators, Tag, Type, Env0, Env
atomic_object_type+Tsemidet
Tinthe unqualified type an _Atomic specifier or qualifier applies to

_Atomic(T) and _Atomic T are the atomic version of T. nanofil takes an integer, a pointer, float or double: on x86-64 each has the size and alignment of T, a power of two of at most 8 bytes, which clang keeps for its atomic version, so the layout is T's. An array or function type is not allowed; a struct, union or long double, whose atomic version clang may pad, realign or make a library call, is refused (bad). Lvalue conversion of an atomic object is an atomic load and simple assignment an atomic store, both sequentially consistent; a compound assignment or increment is an atomic read-modify-write, a cas loop in lowering. Initialization is not atomic. An _Atomic bit-field, which C leaves to the implementation and clang rejects, is refused (bad).

examples 6/6 hold
✓atomic_object_typeint
✓atomic_object_typeptrvoid
✓atomic_object_typedouble
✓atomic_object_typeldouble⟹fails
✓atomic_object_typestruct1⟹fails
✓atomic_object_typearrayint, 2⟹fails
1112atomic_object_typeT ←
1113atomic_object_typeptr_
1114atomic_object_typefloat
1115atomic_object_typedouble
atomic_su_type+S, +Env, -Tsemidet
Sina struct or union type
Envinthe scope S is complete in
Toutatomic(S, P), its atomic version

_Atomic of a struct or union is a type of its own (6.2.5p27): clang rounds a size of at most 8 bytes up to a power of two P and aligns it to P, so its atomic loads and stores are one P-byte access (a 3-byte struct is 4 bytes, aligned 4; an 8-byte one aligned 4 is aligned 8); a store writes the padding as zero. nanofil takes one whose members hold no pointer or vector; a larger one, which clang lowers to libatomic calls, is refused. Lvalue conversion gives S (6.3.2.1p2); a compare-exchange needs S's size to be P already: for a smaller S, clang leaves *expected unchanged when it fails, against 7.17.7.4p2 (its padded copy of *expected is never copied back), so it is refused. a member of an atomic struct or union is not accessed (clang refuses it, -Watomic-access).

1139atomic_su_typeS, Env, atomicS, P ←
S = struct_
or
S = union_
!
catchtype_size_alignS, Env, Size, _, bad_, fail
memberP, 1, 2, 4, 8
P ≥ Size
!
¬holds_refS, Env
holds_ref+T, +Env

T is or holds a pointer or GNU vector.

1146holds_refptr_, _ ←
!
1147holds_refvec_, _, _, _ ←
!
1148holds_refarrayT, _, Env ←
!
holds_refT, Env
1149holds_refatomicT, _, Env ←
!
holds_refT, Env
1150holds_refSU, eO, Tg, Defs, N ←
SU = structId
or
SU = unionId
!
get_assocId, Defs, def_, Members, _, _
memberM, Members
arg2, M, T
holds_refT, eO, Tg, Defs, N
!
atomic_qualified+Tsemidet
Tina reflected type (reflect_type/4)

T is _Atomic at its top level.

examples 2/2 hold
✓atomic_qualifiedqualifiedatomic, int
✓atomic_qualifiedint⟹fails
1161atomic_qualifiedqualifiedQs, _ ←
memberchkatomic, Qs
typeof_type+Arg, -Type, +Env0, -Envdet
Argina typeof specifier's argument: expr(E) or type(TypeName)
Typeoutthe unqualified type it names, as code generation has it
Env0inthe scope it is read in
EnvoutEnv0 with any struct, union or enum TypeName defines

The type of expression E, which is not evaluated, or of TypeName (GNU typeof, C23 typeof). An array or function keeps its type: no conversion applies. Reflection (reflect_type/4) keeps its qualifiers. A variably modified type would need E evaluated for its size, and is refused.

C23 6.7.2.5p4
examples 2/2 hold
✓
bind_ordinarya, varnone, arrayint, 4, E0, E
typeof_typeexprida, T, E, _
⟹T = arrayint, 4
✓
typeof_typeexprbinadd, num1, none−dec, chr65, T, E0, _
⟹T = int
1180typeof_typeexprE, Type, Env, Env ←
expression_typeE, Env, T0
→
→
Type = bad'typeof a variably modified type'
else
unqualifiedT0, Type
else
Type = bad'typeof an expression that cannot be typed'
1187typeof_typetypeTN, Type, Env0, Env ←
type_name_typeTN, Type0, Env0, Env
vm_typeType0
→
Type = bad'typeof a variably modified type'
else
Type = Type0
keyword_type+Keywords, -Type

the type a list of type keywords names.

1192keyword_type[], int

implicit int, as in old headers' K&R style

1193keyword_typeKeyword|Keywords, Type ←
msortKeyword|Keywords, Sorted
keyword_comboSorted, Type0
→
Type = Type0
else
Type = bad'invalid type specifiers'
keyword_combo+SortedKeywords, -Type

the msorted keywords name Type.

1200keyword_combovoid, void
1201keyword_combobool, bool
1202keyword_combochar, char
1203keyword_combochar, signed, schar
1204keyword_combochar, unsigned, uchar
1205keyword_comboS, short ←
memberS, short, short, signed, int, short, int, short, signed
1207keyword_comboS, ushort ←
memberS, short, unsigned, int, short, unsigned
1209keyword_comboS, int ←
memberS, int, signed, int, signed
1211keyword_comboS, uint ←
memberS, unsigned, int, unsigned
1213keyword_comboS, long ←
memberS, long, long, signed, int, long, int, long, signed
1215keyword_comboS, ulong ←
memberS, long, unsigned, int, long, unsigned
1217keyword_comboS, llong ←
memberS, long, long, long, long, signed, int, long, long, int, long, long, signed
1220keyword_comboS, ullong ←
memberS, long, long, unsigned, int, long, long, unsigned
1222keyword_combofloat, float
1223keyword_combodouble, double
1224keyword_combodouble, long, ldouble
1225keyword_combocomplex, struct'builtin.complex.double'

GNU

1226keyword_combocomplex, float, struct'builtin.complex.float'
1227keyword_combocomplex, double, struct'builtin.complex.double'
1228keyword_combocomplex, double, long, struct'builtin.complex.ldouble'
1229keyword_comboS, bad'_Complex' ←
memberchkcomplex, S
1230keyword_comboS, bad'__int128' ←
memberchkint128, S
1231keyword_comboS, bad'_Float16' ←
memberchkfloat16, S
1232keyword_comboS, bad'__bf16' ←
memberchkbf16, S

Structs and unions

su_type+Members, +Kind, +Tag, +Attrs, -Type, +Env0, -Env

Members is none for a reference to the tag, which declares it if it is not yet visible, and otherwise the list of member declarations that define it. Tag is some(Name), or none for an anonymous struct or union; a reference always has a tag.

1241su_type[], Kind, Tag, Attrs, Type, Env0, Env ←
su_definition[], Kind, Tag, Attrs, Type, Env0, Env
1243su_typeMember|Members, Kind, Tag, Attrs, Type, Env0, Env ←
su_definitionMember|Members, Kind, Tag, Attrs, Type, Env0, Env
1245su_typenone, Kind, someName, _, Type, Env0, Env ←
Env0 = e_, Tags, _, _
get_assocName, Tags, tagKind, Id
→
Env = Env0
else
new_tagKind, someName, Id, Env0, Env
Type =.. Kind, Id
su_definition+Members, +Kind, +Tag, +Attrs, -Type, +Env0, -Env

Type is the struct or union Members define, laid out in Env.

1255su_definitionMembers, Kind, Tag, Attrs, Type, Env0, Env ←
Env0 = e_, _, Defs0, _
get_assocexpr_tagKind, Tag, Members, Attrs, Defs0, Id
!
Type =.. Kind, Id
Env = Env0
1259su_definitionMembers, Kind, Tag, Attrs, Type, Env0, Env ←
forward_declaredTag, Kind, Env0, Id
→
Env1 = Env0completes a forward declaration in this scope
else
new_tagKind, Tag, Id, Env0, Env1
Type =.. Kind, Id
foldlmember_decl, Members, Fieldss, Reflectss, Env1, Env2
appendFieldss, Fields
appendReflectss, Reflected
memberAttr, Attrs
→
Packing = packed
memberAttr, Attrs
attr_entryAttr, '__nanofil_pack__', groupintN, _|_
→
Packing = packN
else
Packing = natural
¬definition_alignmentAttrs, Env2, _
→
Def = bad'alignment attributes'
memberchkbadWhy, Fields
→
known_field_prefixFields, Prefix
su_layoutKind, Packing, Prefix, Env2, PrefixDef
PrefixDef = def_, Known, _, _
→
Def = partialWhy, Known
else
Def = badWhy
else
definition_alignmentAttrs, Env2, Request
su_layoutKind, Packing, Fields, Request, Env2, Def
Env2 = eOrds, Tags2, Defs0, Next
put_assocId, Defs0, Def, Defs1
put_assocqmembersId, Defs1, Reflected, Defs2
Kind ≡ union
memberAttr, Attrs
→
put_assoctransparentId, Defs2, t, Defs3
else
Defs3 = Defs2
nb_currentnanofil_expr_tags, true
→
put_assocexpr_tagKind, Tag, Members, Attrs, Defs3, Id, Defs4
else
Defs4 = Defs3
Env = eOrds, Tags2, Defs4, Next
expression_tags+E, +Env0, -Envdet
Eina full expression
Env0inthe scope it is in
EnvoutEnv0 with the structs and unions E's type names define

A type name in an expression may define a struct or union, as krb5's load_16_le reads ((const struct { uint16_t i; } __attribute__((packed)) *)p)->i; the tag is in scope from there to the end of the block (C11 6.2.1p4, 6.7.2.3p6). Elaboration is pure, so the definitions are made first, in Env, and each is recorded against its syntax: elaborating the same type name again finds the same type.

1312expression_tagsE, Env0, Env ←
TNs0 ≡ []
→
Env = Env0
else
b_setvalnanofil_expr_tags, true
foldldefine_type_name, TNs0, Env0, Env
b_setvalnanofil_expr_tags, false
1320define_type_nameTN, Env0, Env ←
catchtype_name_typeTN, _, Env0, Env1, _, fail
!
Env = Env1
1322define_type_name_, Env, Env
defining_type_names+E, -TNs, ?Rest

TNs-Rest are the type names in E, in order, whose specifiers define a struct or union; statement expressions declare in their own blocks.

1327defining_type_namesE, TNs, TNs ←
¬compoundE
!
1328defining_type_namesstmtexpr_, TNs, TNs ←
!
1329defining_type_namestypeSpecs, D, typeSpecs, D|TNs, TNs ←
is_listSpecs
membersu_, _, Ms, _, Specs
Ms ≢ none
!
1331defining_type_namesE, TNs, Rest ←
E =.. _|Args
defining_argsArgs, TNs, Rest
1335defining_args[], TNs, TNs
1336defining_argsA|As, TNs, Rest ←
defining_argsAs, TNs1, Rest
definition_alignment+Attrs, +Env, -Alignsemidet
Attrsinthe attributes of a struct or union definition
Envinthe scope it is defined in
Alignoutthe alignment its aligned attributes request, 1 if none

GNU's aligned(N) on a struct or union definition makes its alignment the larger of N and its members' (packed or not); bare aligned means the target's biggest alignment, 16 on x86-64 (clang's __BIGGEST_ALIGNMENT__). N must be a constant power of two. Fails for an alignment above 4096, which nanofil does not give objects (max_alignment/1), or a non-constant N.

GCC manual, Common Type Attributes, alignedC11 6.2.8p4
examples 5/5 hold
✓
definition_alignmentgroupidpacked, p(','), idaligned, groupint4, none−dec, E, A
⟹A = 4
✓
definition_alignmentgroupid'__aligned__', E, A
⟹A = 16
✓⟹A = 1
✓
definition_alignmentgroupidaligned, groupint8192, none−dec, E, _
⟹fails
✓
definition_alignmentgroupidaligned, groupint12, none−dec, E, _
⟹fails
1364definition_alignmentAttrs, Env, Align ←
findallR,
memberAttr, Attrs
,
Requests
foldlrequested_alignmentEnv, Requests, 1, Align
aligned_entry+Tokens, -Requestnondet

Request is bare, or arg(Ts) with the argument tokens Ts, for each aligned attribute of an attribute's Tokens (attr_entry/3).

1371aligned_entryTokens, Request ←
attr_entryTokens, Word, Rest
memberchkWord, aligned, '__aligned__'
Rest = groupTs|_
→
Request = argTs
else
Request = bare
1376requested_alignment_, bare, A0, A ←
A is maxA0, 16
1377requested_alignmentEnv, argTs, A0, A ←
phraseflat_tokensTs, Flat
catchconst_valueE, Env, N, _, not_constant, fail
N > 0
N /\ N−1 =:= 0
N ≤ M
A is maxA0, N
aligned_def+Def0, +Align, -Def

Def is the definition Def0 with its alignment at least Align and its size rounded up to that.

1386aligned_defdefKind, Members, Size0, Align0, Request, defKind, Members, Size, Align ←
!
Align is maxAlign0, Request
Size is Size0 + Align−1//Align × Align
1389aligned_defDef, _, Def
bit_castable+From, +To, +Envsemidet
Frominthe type of __builtin_bit_cast's operand, after conversion
Tointhe type it names
Envinthe scope

Both are complete object types of one size, and To is not an array (clang's rule; C has no __builtin_bit_cast). The result has the operand's bytes, as memcpy into a To would give it (C11 7.24.2.1): a pointer's bytes keep its address and leave its capability, and a pointer made from bytes has none, as in Fil-C's clang.

C11 6.2.6.1p4Clang Language Extensions, __builtin_bit_cast
examples 3/3 hold
✓
bit_castablefloat, uint, E
✓⟹fails
✓
bit_castablelong, arraychar, 8, E
⟹fails
1406bit_castableFrom, To, Env ←
To ≠ array_, _
To ≠ func_, _, _
To ≢ void
catch
type_size_alignFrom, Env, Size, _
type_size_alignTo, Env, Size, _
,
bad_, fail

A later unsupported member (notably glibc FILE's _flags2 bitfield) does not change preceding offsets. Never infer offsets beyond it, or a size/alignment for the whole object; those uses must still reject.

1414known_field_prefix[], []
1415known_field_prefixbad_|_, [] ←
!
1416known_field_prefixField|Fields, Field|Prefix ←
known_field_prefixFields, Prefix
forward_declared+Tag, +Kind, +Env, -Id

Tag names the Kind with Id, which is declared but not yet defined. An anonymous one never is.

1421forward_declaredsomeName, Kind, e_, Tags, Defs, _, Id ←
get_assocName, Tags, tagKind, Id
get_assocId, Defs, incomplete_
1425new_tagKind, Tag, Id, eOrds, Tags0, Defs0, Id, eOrds, Tags, Defs, Next ←
Next is Id + 1
tag_boundTag, tagKind, Id, Tags0, Tags
put_assocId, Defs0, incompleteTag, Defs
tag_bound+Tag, +Value, +Tags0, -Tags

Tags is Tags0 with the name of Tag bound to Value; an anonymous Tag binds nothing.

1432tag_boundnone, _, Tags, Tags
1433tag_boundsomeName, Value, Tags0, Tags ←
put_assocName, Tags0, Value, Tags
member_decl+Decl, -Fields, -Reflected, +Env0, -Envdet
Declina field(Specs, Dcl, Width) of c_parse, or a static_assert
Fieldsoutthe f(Name, Type) or b(Name, Type, Width) it declares, [] for a static assertion, or [bad(Why)]
Reflectedoutthe q(Name, QType) of a named member, QType keeping the qualifiers Type drops
Env0inthe scope the struct or union is defined in
EnvoutEnv0 with what the specifiers define and the base type kept

Name is some(Field), or none for an anonymous member or an unnamed bit-field. The declarators of one member declaration share its specifiers' type: c_parse marks each after the first shared_base, and it takes the base type the previous field kept as enclosing(member_base), so struct { ... } a, b; declares one struct and a = b assigns.

examples 2/2 hold
✓
S = sustruct, none, fieldtspecint, dclsomek, base, none, none, []
member_declfieldS, dclsomea, base, none, none, f_, TA, _, E0, E1
member_declfieldshared_base, S, dclsomeb, base, none, none, f_, TB, _, E1, _
⟹TA ≡ TB
✓
S = sustruct, none, fieldtspecint, dclsomek, base, none, none, []
member_declfieldS, dclsomea, base, none, none, f_, TA, _, E0, E1
member_declfieldS, dclsomeb, base, none, none, f_, TB, _, E1, _
⟹TA ≢ TB
1460member_declstatic_assertLoc, Expr, _, [], [], Env, Env ←
C11 6.7.10p2
catchconst_valueExpr, Env, V, _, not_constant, fail
→
V ≠ 0
→
true
else
sema_errorLoc, 'static assertion failed', []
else
sema_errorLoc, 'static assertion is not a constant', []
1465member_declfieldSpecs00, dclName, SyntaxType, _, Width, Fields, Reflected, Env0, Env ←
selectchkshared_base, Specs00, Specs06.7.2.1p1, 6.7.2.3p5
→
lookup_ordinaryEnv0, enclosingmember_base, Base
Env1 = Env0
else
Specs0 = Specs00
alignment_requestsSpecs0, Requests, Specs1
member_packingSpecs1, Specs, Packed
nonvarBase
→
true
else
specs_typeSpecs, Base, Env0, Env1a
bind_ordinaryenclosingmember_base, Base, Env1a, Env1
apply_declaratorSyntaxType, Base, Type, Env1, Env
Packed ≡ yes
→
Inherited = align1
else
inherited_alignmentSpecs, SyntaxType, Env, Inherited
member_alignmentRequests, Inherited, Env, MA
bad_typeType, Why
→
Fields = badWhy
Reflected = []
MA = badWhy
→
Fields = badWhy
Reflected = []
Width ≢ none
MA ≢ none
→
Fields = bad'alignment attributes'
Reflected = []
vm_typeType6.7.2.1p9
→
Fields = bad'variably modified member'
Reflected = []
Width ≢ none
¬bitfield_widthWidth, Name, Type, Env, _
→
Fields = bad'invalid bit-field'
Reflected = []
Width ≢ none6.7.2.1p5
reflect_typetypeSpecs, SyntaxType, Type, Env, QT
→
Fields = bad'_Atomic bit-field'
Reflected = []
else
Width ≡ none
MA ≡ none
→
Fields = fName, Type
Width ≡ none
→
Fields = fName, Type, MA
else
bitfield_widthWidth, Name, Type, Env, W
Fields = bName, Type, W
Width ≢ none
Name ≡ none
→
Reflected = []
else
reflect_typetypeSpecs, SyntaxType, Type, Env, QType
Reflected = qName, QType
member_alignment+Requests, +Inherited, +Env, -MAdet
Requestsina member's _Alignas specifiers and aligned attributes
Inheritedin[align(I)] if its type is a typedef of alignment I (inherited_alignment/4), else []
Envinthe scope it is declared in
MAoutnone, a(Base, Explicit) or bad(Reason)

The member's alignment is Explicit, the strictest request (1 if none), or Base if that is stricter: its typedef's alignment, or natural, its type's own. _Alignas and aligned cannot weaken a member (6.7.5p4), but a typedef's alignment can (GCC's aligned typedefs). In a packed struct only Explicit counts. Above 4096, or with a request that is not a constant power of two, the member is refused (bad).

C11 6.7.5p4GCC manual, Common Type Attributes, alignedGCC manual, Common Variable Attributes, aligned
examples 4/4 hold
✓⟹MA = none
✓
member_alignmentattrgroupidaligned, groupint32, none−dec, [], E, MA
⟹MA = anatural, 32
✓
member_alignment[], align1, E, MA
⟹MA = a1, 1
✓
member_alignmentalignasexprnum8192, none−dec, [], E, MA
⟹MA = bad'an alignment above 4096'
1528member_alignment[], [], _, none ←
!
1529member_alignmentRequests, Inherited, Env, MA ←
Inherited = alignB
→
Base = B
else
Base = natural
maplistrequest_alignmentEnv, Requests, As
→
max_list1|As, Explicit
Explicit > M
→
MA = bad'an alignment above 4096'
else
MA = aBase, Explicit
else
MA = bad'alignment attributes'
member_packing+Specs0, -Specs, -Packed

Packed is yes when a member's attributes include packed, which GCC gives the member the least alignment, 1, unless aligned asks for more (Common Variable Attributes, packed): its alignment base is 1 (member_alignment/4). Specs are Specs0 without the packed items.

1544member_packingSpecs0, Specs, Packed ←
selectattrgroupInner, Specs0, Rest
attr_itemsInner, Items
selectT, Items, Others
T = idWord
or
T = kwWord
memberchkWord, packed, '__packed__'
→
Packed = yes
Others ≡ []
→
Specs1 = Rest
else
attr_itemsJoined, Others
Specs1 = attrgroupJoined|Rest
member_packingSpecs1, Specs, _
else
Specs = Specs0
Packed = no
alignment_requests+Specs0, -Requests, -Specsdet
Specs0ina declaration's specifiers and attributes
Requestsoutits alignment requests: each _Alignas, and each aligned attribute as attr([group(Item)])
SpecsoutSpecs0 without them

One __attribute__((...)) may list several attributes, separated by commas, each applying as if alone: GLib's resources declare their data with section(...), aligned(...) together. An aligned item is a request and the group's other items stay with the specifiers, so the section still places the object.

GCC manual, Attribute Syntax
examples 3/3 hold
✓alignment_requestsattrgroupidsection, groupstr46, 114, p(','), idaligned, groupint8, none−dec, tspecint, R, S⟹R = attrgroupidaligned, groupint8, none−dec, S = attrgroupidsection, groupstr46, 114, tspecint
✓alignment_requestsattrgroupid'__aligned__', alignasexprnum4, none−dec, R, S⟹R = attrgroupid'__aligned__', alignasexprnum4, none−dec, S = []
✓alignment_requestsattrgroupidpacked, tspecchar, R, S⟹R = [], S = attrgroupidpacked, tspecchar
1577alignment_requests[], [], []
1578alignment_requestsalignasA|Ss0, alignasA|Rs, Ss ←
!
1580alignment_requestsattrgroupTokens|Ss0, Rs, Ss ←
!
attr_itemsTokens, Items
partitionaligned_attr, Items, Aligned, Others
findallattrgroupI, memberI, Aligned, Rs1
Others ≡ []
→
Ss = Ss1
else
attr_itemsRest, Others
Ss = attrgroupRest|Ss1
appendRs1, Rs2, Rs
alignment_requestsSs0, Rs2, Ss1
1590alignment_requestsattrTokens|Ss0, attrTokens|Rs, Ss ←
1593alignment_requestsS|Ss0, Rs, S|Ss ←
attr_items?Tokens, ?Items

Tokens are the Items joined by commas.

1597attr_itemsTokens, I|Is ←
nonvarTokens
!
appendI, p(',')|Rest, Tokens
→
attr_itemsRest, Is
else
I = Tokens
Is = []
1602attr_itemsTokens, I ←
!
Tokens = I
1603attr_itemsTokens, I|Is ←
attr_itemsRest, Is
appendI, p(',')|Rest, Tokens
attribute_expression+Flat, +Env, -E

E is the constant expression the attribute argument tokens Flat are, read with the typedef names of Env that they use.

1608attribute_expressionFlat, Env, E ←
findallN,
membertokidN, _, Flat
lookup_ordinaryEnv, N, typedef_
,
Ns0
sortNs0, Ns
catchc_constant_expressionFlat, Ns, E, nanofil_error_, fail
flat_tokens+Tokens//

attribute Tokens, groups parenthesized again, as c_lex tokens.

1615flat_tokens[] ⟶
[]
1616flat_tokensgroupTs|Rest ⟶
!
tokp'(', attr
tokp')', attr
1618flat_tokensT|Rest ⟶
tokT, attr
bitfield_width+Expr, +Name, +Type, +Env, -W

a bit-field Name of Type may have the width Expr, of value W: an integer constant no wider than Type, and 0 only without a name (6.7.2.1p4).

1623bitfield_widthExpr, Name, Type, Env, W ←
catchconst_valueExpr, Env, W, _, not_constant, fail
int_infoType, Size, _
Type ≡ bool
→
Max = 1
else
Max is 8 × Size
W ≥ 0
W ≤ Max
W =:= 0
→
Name ≡ none
else
true
su_layout+Kind, +Packing, +Fields, +Env, -Defdet
Kindinstruct or union
Packinginnatural, packed for GNU's packed attribute, or pack(N) under #pragma pack(N)
Fieldsinthe f(Name, Type) and bit-fields b(Name, Type, Width) in order
Envinthe scope defining the members' types
Defoutthe definition: def(Kind, Members, Size, Align), or bad(Why)

The SysV rules: each member at the next multiple of its alignment; size rounded up to the alignment. A trailing flexible array member has size 0. A bit-field follows the bits before it unless it would cross a boundary of its type's size, where it starts instead; a zero-width one only moves to that boundary. Unnamed bit-fields do not align the struct. Packed, every member has alignment 1, and so has the struct, and a bit-field follows the bits before it across any boundary (a zero-width one still moves to one), as GCC and clang lay it out; a packed bit-field that no access of its type within the struct covers is accessed in the narrowest integer of its sign that does, as clang's narrower access, when its type is no wider than int, else refused. A packed pointer is laid out as any member: Fil-C keeps a capability only at an 8-aligned address, and each access of a pointer checks at run time that its address is 8-aligned, as filcc's does ("alignment requirement of 8 bytes not met"), so libmbim's and elfutils' packed structs work where they are placed at an 8-aligned address and parted's GPT header, whose pointer is at 92, traps where it is accessed. Under #pragma pack(N) (c_pp marks the definition) each member's alignment is at most N, as GCC's and clang's, a pointer's too; a bit-field or an explicitly aligned member that N would underalign is refused. C11 leaves the alignment of members implementation-defined.

examples 14/14 hold
✓
su_layoutstruct, natural, fsomea, char, fsomeb, int, E, D
⟹D = defstruct, msomea, char, 0, msomeb, int, 4, 8, 4
✓
su_layoutstruct, packed, fsomea, char, fsomeb, int, E, D
⟹D = defstruct, msomea, char, 0, msomeb, int, 1, 5, 1
✓
su_layoutunion, packed, fsomea, char, fsomeb, long, E, D
⟹D = defunion, msomea, char, 0, msomeb, long, 0, 8, 1
✓
su_layoutstruct, packed, fsomec, char, fsomep, ptrint, E, D
⟹D = defstruct, msomec, char, 0, msomep, ptrint, 1, 9, 1
✓
su_layoutstruct, packed, fsomep, ptrint, fsomec, char, 8, E, D
⟹D = defstruct, msomep, ptrint, 0, msomec, char, 8, 16, 8
✓
su_layoutstruct, packed, fsomec, char, fsomep, ptrint, 8, E, D
⟹D = defstruct, msomec, char, 0, msomep, ptrint, 1, 16, 8
✓
su_layoutstruct, packed, fsomea, char, bsomex, int, 30, fsomeb, char, E, D
⟹D = defstruct, msomea, char, 0, msomex, int, 1, bits0, 30, msomeb, char, 5, 6, 1
✓
su_layoutstruct, packed, fsomea, char, bnone, int, 0, fsomeb, char, E, D
⟹D = defstruct, msomea, char, 0, msomeb, char, 4, 5, 1
✓
su_layoutstruct, pack2, fsomea, char, fsomeb, int, E, D
⟹D = defstruct, msomea, char, 0, msomeb, int, 2, 6, 2
✓
su_layoutstruct, pack8, fsomea, char, fsomep, ptrint, E, D
⟹D = defstruct, msomea, char, 0, msomep, ptrint, 8, 16, 8
✓
su_layoutstruct, pack4, fsomec, char, fsomep, ptrint, E, D
⟹D = defstruct, msomec, char, 0, msomep, ptrint, 4, 12, 4
✓
su_layoutstruct, packed, bsomex, uint, 6, bsomef, uint, 2, E, D
⟹D = defstruct, msomex, uchar, 0, bits0, 6, msomef, uchar, 0, bits6, 2, 1, 1
✓
su_layoutstruct, packed, bsomex, int, 3, E, D
⟹D = defstruct, msomex, schar, 0, bits0, 3, 1, 1
✓
su_layoutstruct, packed, bsomex, long, 3, E, D
⟹D = bad'a packed bit-field that no access of its type covers'
1691su_layoutKind, Packing, Fields, Env, Def ←
su_layoutKind, Packing, Fields, 1, Env, Def
su_layout+Kind, +Packing, +Fields, +Request, +Env, -Def

su_layout/5 with the definition aligned to at least Request (its aligned attribute).

1697su_layoutKind, Packing, Fields, Request, Env, Def ←
catchfields_layoutKind, Packing, Fields, Env, Layout, badWhy, Layout = badWhy
layout_defLayout, Kind, Def0
aligned_defDef0, Request, Def
fields_layout+Kind, +Packing, +Fields, +Env, -Layout

Layout is placed(Members, End, Align) for Fields laid out in Env, or incomplete when a field has no size. Throws bad(Why) as type_size_align/4.

1706fields_layoutKind, Packing, Fields, Env, Layout ←
foldlfield_memberKind, Packing, Env, Fields, Memberss, 0−1, EndBits−Align
→
appendMemberss, Members0
End is EndBits + 7//8
maplistpacked_unitEnd, Env, Members0, Members
Layout = placedMembers, End, Align
else
Layout = incomplete
layout_def+Layout, +Kind, -Def

Def is the definition of a struct or union of Kind with Layout, its size rounded up to its alignment.

1717layout_defplacedMembers, End, Align, Kind, defKind, Members, Size, Align ←
Size is End + Align−1//Align × Align
1719layout_defincomplete, _, bad'incomplete member'
1720layout_defbadWhy, _, badWhy
field_member+Kind, +Env, +Field, -Members, +End0−Align0, −End−Align

Members, the member Field declares or none for an unnamed bit-field, are placed after members ending at bit End0 with alignment Align0; End and Align include them. Throws bad(Why) as type_size_align/4. Any type but a flexible array (array(_, none)) has its own size, so the if-then-else keeps that default case.

1728field_memberKind, Packing, Env, fName, Type, mName, Type, Off, End0−Align0, End−Align ←
Type = arrayElemType, none
→
type_size_alignElemType, Env, _, NaturalAlign
FieldSize = 0
else
type_size_alignType, Env, FieldSize, NaturalAlign
Packing ≡ packed
→
FieldAlign = 1
Packing = packN
N < NaturalAlign
→
FieldAlign = N
else
FieldAlign = NaturalAlign
member_offset_endKind, End0, FieldSize, FieldAlign, Off, End
Align is maxAlign0, FieldAlign
1742field_memberKind, Packing, Env, fName, Type, aBase, Explicit, mName, Type, Off, End0−Align0, End−Align ←
Type = arrayElemType, none
→
type_size_alignElemType, Env, _, NaturalAlign
FieldSize = 0
else
type_size_alignType, Env, FieldSize, NaturalAlign
Base ≡ natural
→
BaseAlign = NaturalAlign
else
BaseAlign = Base
Packing = packN
N < maxBaseAlign, Explicit
→
throwbad'#pragma pack of an explicitly aligned member'
else
true
Packing ≡ packed
→
FieldAlign = Explicit
else
FieldAlign is maxBaseAlign, Explicit
member_offset_endKind, End0, FieldSize, FieldAlign, Off, End
Align is maxAlign0, FieldAlign
1759field_memberKind, natural, Env, bName, Type, W, Members, End0−Align0, End−Align ←
type_size_alignType, Env, Size, FieldAlign
UnitBits is 8 × Size
bitfield_start_endKind, End0, W, UnitBits, Start, End
Name ≡ none
→
Members = []
Align = Align0
else
Unit is Start//UnitBits × Size
Pos is Start mod UnitBits
Members = mName, Type, Unit, bitsPos, W
Align is maxAlign0, FieldAlign
1770field_memberKind, packN, Env, bName, Type, W, Members, End0−Align0, End−Align ←
!
type_size_alignType, Env, _, FieldAlign
N ≥ FieldAlign
→
field_memberKind, natural, Env, bName, Type, W, Members, End0−Align0, End−Align
else
throwbad'#pragma pack of a bit-field'

A packed bit-field follows the bits before it, crossing any boundary; a zero-width one still moves to its type's boundary (clang's layout). Its unit, placed by packed_unit/4 once the size is known, is a window of its type's size within the struct.

1780field_memberKind, packed, Env, bName, Type, W, Members, End0−Align, End−Align ←
type_size_alignType, Env, Size, _
UnitBits is 8 × Size
W =:= 0
→
bitfield_start_endKind, End0, 0, UnitBits, Start, End
Kind ≡ union
→
Start = 0
End is maxEnd0, W
else
Start = End0
End is Start + W
Name ≡ none
→
Members = []
else
Members = packed_bitsName, Type, Start, W
packed_unit+End, +Env, +Member0, -Member

Member is Member0, a packed bit-field's packed_bits(Name, Type, Start, W) placed in the unit of Type's size that starts at the latest byte at or before bit Start and ends within the struct's End bytes, as an access to it must. Where none does (parted's one-byte FatClusterInfo of unsigned int:6 and an enum :2), the unit is the narrowest integer of Type's sign that does, as clang accesses the field in a narrower integer: for a type no wider than int, the field's values and their promotion to int are the same. Else it is bad.

1800packed_unitEnd, Env, packed_bitsName, Type, Start, W, mName, Unit, Off, bitsPos, W ←
!
type_size_alignType, Env, Size, _
packed_windowEnd, Start, W, Size, Off0, Pos0
→
Unit = Type
Off = Off0
Pos = Pos0
Size ≤ 4
int_infoType, _, Sign
memberN−Narrow, 1−uchar, 1−schar, 2−ushort, 2−short
N < Size
int_infoNarrow, N, Sign
packed_windowEnd, Start, W, N, Off1, Pos1
→
Unit = Narrow
Off = Off1
Pos = Pos1
else
throwbad'a packed bit-field that no access of its type covers'
1811packed_unit_, _, M, M
packed_window+End, +Start, +W, +Size, -Off, -Pos

the Size bytes at Off, the latest that start at or before bit Start and end within End bytes, hold the W bits from bit Pos.

1816packed_windowEnd, Start, W, Size, Off, Pos ←
Off is minStart//8, End−Size
Pos is Start−8 × Off
Off ≥ 0
Pos + W ≤ 8 × Size
unaligned_type+T, +Envsemidet
Tina struct or union type
Envinthe scope defining it

T has a member, perhaps nested, at an offset that is not a multiple of its type's alignment, as only a packed struct can. The SysV ABI then classifies T as MEMORY: passed and returned in memory.

SysV x86-64 psABI 3.2.3
examples 3/3 hold
✓
su_definitionfieldtspecchar, dclsomea, base, none, none, fieldtspecint, dclsomeb, base, none, none, struct, none, groupidpacked, T, E0, E
✓
su_definitionfieldtspecint, dclsomeb, base, none, none, fieldtspecchar, dclsomea, base, none, none, struct, none, groupidpacked, T, E0, E
⟹fails
✓
unaligned_typestruct'builtin.va_list', E
⟹fails
1838unaligned_typeSU, eO, Tg, Defs, N ←
SU = structId
or
SU = unionId
!
get_assocId, Defs, def_, Members, _, _
memberM, Members
M = m_, T, Off
catchtype_size_alignT, eO, Tg, Defs, N, _, Align, bad_, fail
Off mod Align ≠ 0
→
true
else
unaligned_typeT, eO, Tg, Defs, N
!
1844unaligned_typearrayT, _, Env ←
underaligned_member+X, +Env, +Width, -Namesemidet
Xinan object tree
Envinthe scope of X
Widthinthe byte width of an atomic access to X
Nameoutthe member, perhaps containing X, that may be misaligned

X is a member, perhaps nested, of a struct or union aligned to less than Width, or at an offset that is not a multiple of it, as only a packed one can be: clang then lowers an atomic read-modify-write of X to libatomic's lock-based calls, which nanofil does not have, so lowering refuses it rather than trap on Fil-C's alignment check.

examples 2/2 hold
✓
su_definitionfieldtspecchar, dclsomec, base, none, none, fieldtspecint, dclsomex, base, none, none, struct, none, groupidpacked, T, E0, E
underaligned_memberobjint, memberobjT, idps, x, E, 4, N
⟹N = x
✓
su_definitionfieldtspecchar, dclsomec, base, none, none, fieldtspecint, dclsomex, base, none, none, struct, none, [], T, E0, E
underaligned_memberobjint, memberobjT, ids, x, E, 4, _
⟹fails
1867underaligned_memberobj_, memberXS, F, Env, W, Name ←
tree_typeXS, SU0
catchtype_size_alignSU, Env, _, A, bad_, fail
A < W
→
Name = F
member_layoutSU, F, Env, _, Off, _
Off mod W ≠ 0
→
Name = F
else
underaligned_memberXS, Env, W, Name
member_alignment+X, +Env, -Asemidet
Xinan object tree that is a member, perhaps nested
Envinthe scope of X
Aoutthe alignment an access to X is known to have

A member's alignment is its struct or union's, or its own containing member's, at the member's offset: the largest power of two dividing the offset, at most that base alignment. A packed struct's members are known to be aligned to 1 only. clang gives an access this alignment, and filcc checks a vector access against it (capped at 8; docs/vector-access.md), so _mm_loadu_si128's member of its packed struct is checked at alignment 1.

GCC manual, Common Type Attributes, packedC11 6.7.2.1p15
examples 2/2 hold
✓
su_definitionfieldtspecchar, dclsomec, base, none, none, fieldtspeclong, dclsomex, base, none, none, struct, none, groupidpacked, T, E0, E
member_alignmentobjlong, memberobjT, idps, x, E, A
⟹A = 1
✓
su_definitionfieldtspecint, dclsomec, base, none, none, fieldtspecint, dclsomex, base, none, none, struct, none, [], T, E0, E
member_alignmentobjint, memberobjT, ids, x, E, A
⟹A = 4
1898member_alignmentobj_, memberXS, F, Env, A ←
tree_typeXS, SU0
catchtype_size_alignSU, Env, _, SA, bad_, fail
member_alignmentXS, Env, B0
→
B is minB0, SA
else
B = SA
member_layoutSU, F, Env, _, Off, _
Off =:= 0
→
A = B
else
A is minB, Off /\ −Off
member_offset_end+Kind, +End0, +Size, +Align, -Off, -End

a member of Size and Align after members ending at bit End0 is at byte Off and ends the struct or union so far at bit End.

1908member_offset_endstruct, End0, Size, Align, Off, End ←
Off is End0 + 7//8 + Align−1//Align × Align
End is 8 × Off + Size
1911member_offset_endunion, End0, Size, _, 0, End ←
End is maxEnd0, 8 × Size
bitfield_start_end+Kind, +End0, +W, +UnitBits, -Start, -End

a bit-field of W bits, in units of UnitBits, after bits ending at End0 occupies bits Start to End.

1917bitfield_start_endunion, End0, W, _, 0, End ←
End is maxEnd0, W
1918bitfield_start_endstruct, End0, W, UnitBits, Start, End ←
W > 0
End0//UnitBits =:= End0 + W−1//UnitBits
→
Start = End0
else
Start is End0 + UnitBits−1//UnitBits × UnitBits
End is Start + W

Enums

enum_type+Enumerators, +Tag, -Type, +Env0, -Env

Type is the enum's underlying type. Enumerators is none for a reference to Tag, and otherwise the list that defines it; a reference to an unknown enum tag has type uint. Tag is some(Name) or none, as in su_type/7.

1931enum_typepackedEnumerators, Tag, Type, Env0, Env ←
!
enum_definitionpacked, Enumerators, Tag, Type, Env0, Env
1933enum_type[], Tag, Type, Env0, Env ←
enum_definition[], Tag, Type, Env0, Env
1935enum_typeEnumerator|Enumerators, Tag, Type, Env0, Env ←
enum_definitionEnumerator|Enumerators, Tag, Type, Env0, Env
1937enum_typenone, someName, Type, Env, Env ←
Env = e_, Tags, _, _
get_assocName, Tags, enumType0
→
Type = Type0
else
Type = uint
enum_definition+Enumerators, +Tag, -Type, +Env0, -Env

Type is the underlying type of the enum Enumerators define; Env binds its constants and its tag. enum_definition/6 with packed gives it the packed underlying type.

1948enum_definitionEnumerators, Tag, Type, Env0, Env ←
enum_definitionnatural, Enumerators, Tag, Type, Env0, Env
1950enum_definitionPacking, Enumerators, Tag, Type, Env0, Env ←
foldlenumerator, Enumerators, Values, Env0−0, Env1−_
Packing ≡ packed
→
else
enum_underlyingValues, Type
Env1 = eOrds, Tags0, Defs, Next
tag_boundTag, enumType, Tags0, Tags
Env = eOrds, Tags, Defs, Next
enumerator+Enumerator, -V, +Env0−Next0, −Env−Next

V is the constant's value, Next0 when it has no expression, and Env binds it. Each constant has type int when it fits, as C requires of constants whose values fit; the enum's own type follows clang.

1963enumeratorenumeratorName, Expr, V, Env0−Next, Env−Next1 ←
Expr ≡ none
→
V = Next
else
const_valueExpr, Env0, V, _
Next1 is V + 1
V ≥ -2147483648
V ≤ 2147483647
→
ConstType = int
else
ConstType = long
bind_ordinaryName, enumconstV, ConstType, Env0, Env
enum_underlying+Values, -Typedet

Type is the type clang gives an enum whose constants have Values: the first of uint, int, ulong and long that holds them all (uint when there are none).

1979enum_underlying[], uint
1980enum_underlyingV|Vs, Type ←
min_listV|Vs, Lo
max_listV|Vs, Hi
Lo ≥ 0
Hi ≤ 4294967295
→
Type = uint
Lo ≥ -2147483648
Hi ≤ 2147483647
→
Type = int
Lo ≥ 0
→
Type = ulong
else
Type = long
packed_enum_underlying+Values, -Typedet
Valuesinthe values of an enum's constants
Typeoutits underlying type under GNU's packed attribute

The smallest of uchar, ushort, uint and ulong that holds every value when none is negative, else of schar, short, int and long, as GCC and clang choose (uchar when there are none). C11 leaves the choice of an enumerated type's compatible integer type implementation- defined, as long as it can represent every member.

examples 5/5 hold
✓packed_enum_underlying0, 1, 2, T⟹T = uchar
✓packed_enum_underlying0, 256, T⟹T = ushort
✓packed_enum_underlying-1, 127, T⟹T = schar
✓packed_enum_underlying-1, 128, T⟹T = short
✓packed_enum_underlying[], T⟹T = uchar
2004packed_enum_underlying[], uchar ←
!
2005packed_enum_underlyingVs, Type ←
min_listVs, Lo
max_listVs, Hi
Lo ≥ 0
→
memberType−Max, uchar−255, ushort−65535, uint−4294967295, ulong−18446744073709551615
Hi ≤ Max
else
memberType−Min−Max, schar−-128−127, short−-32768−32767, int−-2147483648−2147483647, long−-9223372036854775808−9223372036854775807
Lo ≥ Min
Hi ≤ Max
!

Declarators

apply_declarator+SyntaxType, +Base, -Type, +Env0, -Envdet
SyntaxTypeina declarator's type, built by the parser outermost first around the atom base
Baseinthe type the declaration specifiers name
Typeoutthe declared type
Env0inthe scope
EnvoutEnv0 with the struct and union definitions of any parameter list

Array sizes are evaluated and parameter types resolved. A size that is not an integer constant expression, or [*], makes a variable length array, whose size expression lowering evaluates; a negative constant size is bad. Parameter names are in scope for the size expressions of the parameters after them, and only there.

examples 5/5 hold
✓
apply_declaratorarraybase, num3, none−dec, int, T, E, _
⟹T = arrayint, 3
✓
apply_declaratorarraybase, idn, int, T, E, _
⟹T = arrayint, vlaidn
✓
apply_declaratorptrarraybase, star, [], int, T, E, _
⟹T = ptrarrayint, vlastar
✓
apply_declaratorarraybase, unneg, num1, none−dec, int, T, E, _
⟹T = bad'negative array size'
✓
bind_ordinaryn, enumconst4, int, E0, E
apply_declaratorfuncbase, paramsparamtspecint, dclsomen, base, none, paramtspecint, dclsomea, ptrarraybase, idn, [], none, fixed, int, T, E, _
⟹T = funcint, int, ptrarrayint, vlaidn, fixed
2045apply_declaratorbase, Base, Base, Env, Env
2046apply_declaratorptrSyntax, _, Base, ptrType, Env0, Env ←
apply_declaratorSyntax, Base, Type, Env0, Env
2048apply_declaratorarraySyntax, SizeExpr, Base, Type, Env0, Env ←
apply_declaratorSyntax, Base, ElemType, Env0, Env
SizeExpr ≡ none
→
Type = arrayElemType, none
SizeExpr ≡ star
→
Type = arrayElemType, vlastar
catchconst_valueSizeExpr, Env, N, _, not_constant, fail
→
N ≥ 0
→
Type = arrayElemType, N
else
Type = bad'negative array size'
else
Type = arrayElemType, vlaSizeExpr
2058apply_declaratorfuncSyntax, Params, Base, funcRet, ParamTypes, Variadic, Env0, Env ←
apply_declaratorSyntax, Base, Ret, Env0, Env1
param_typesParams, ParamTypes, Variadic, Env1, Env
param_types+Params, -Types, -Variadic, +Env0, -Env

Types are the adjusted parameter types. Parameter declarations are scoped to the parameter list.

2066param_typesunspecified, unspecified, fixed, Env, Env
2067param_typesknr_, _, _, _, _ ←
6.7.6.3p3
thrownanofil_error'a parameter list without types is only allowed in a function definition'
2069param_typesparamsParams, Variadic, Types, Variadic, Env0, Env ←
env_scopeEnv0, Scope, _
foldlparam_type, Params, Types, Env0, Env1
env_restoreScope, Env1, Env
2074param_typeparamSpecs, dclName, SyntaxType, _, Type1, Env0, Env ←
specs_typeSpecs, Base, Env0, Env1
apply_declaratorSyntaxType, Base, Type0, Env1, Env2
type_adjustedType0, Type1
Name = someN
→
bind_ordinaryN, varnone, Type1, Env2, Env
else
Env = Env2
knr_definition+Loc, +Knr, +Prototyped, +Env, -Params, -Localsdet
Locinthe definition's location
Knrinknr(Names, Decls): a K&R definition's identifier list and the declarations of its parameters
Prototypedintrue if a prototype of the function is in scope, else false
Envinthe file scope
Paramsoutparams(Ps, fixed), the parameters of the equivalent prototyped definition
Localsoutthe declarations its body starts with

Without a prototype in scope a caller passes each argument default- promoted, so a parameter whose declared type promotion changes (char, short, _Bool, float) comes in as the promoted type, under a name no source can spell, and the body starts with a local of the declared type initialized from it. clang gives the function the signature of the promoted types; with a prototype in scope it keeps the declared ones, as this does. A name with no declaration is int in GNU89 mode (NANOFIL_STD=gnu89) and refused otherwise with clang's message, as is a declaration of a name not in the list.

examples 2/2 hold
✓
knr_definitionl, knra, p, decll, tspecchar, initdclsomea, base, none, none, decll, tspecchar, initdclsomep, ptrbase, [], none, none, false, E, Ps, Ls
⟹Ps = paramsparamtspecint, dclsome'%knr_a', base, none, paramtspecchar, dclsomep, ptrbase, [], none, fixed, Ls = decll, tspecchar, initdclsomea, base, none, id'%knr_a'
✓
knr_definitionl, knrb, decll, tspecfloat, initdclsomeb, base, none, none, true, E, Ps, Ls
⟹Ps = paramsparamtspecfloat, dclsomeb, base, none, fixed, Ls = []
2113knr_definitionLoc, knrNames, Decls, Prototyped, Env, paramsParams, fixed, Locals ←
findallN−pDLoc, Specs, Syn, Init,
memberdeclDLoc, Specs, Inits, Decls
memberinitdclsomeN, Syn, _, Init, Inits
,
Declared
forallmemberN−p_, _, _, Init, Declared,
¬memberchkN, Names
→
sema_errorLoc, 'parameter named \'~w\' is missing', N
Init ≢ none
→
sema_errorLoc, 'C does not support default arguments', []
else
true
maplistknr_paramLoc, Declared, Prototyped, Env, Names, Params, Localss
appendLocalss, Locals
2128knr_paramLoc, Declared, Prototyped, Env, N, Param, Locals ←
memberchkN−pDLoc, Specs, Syn, _, Declared
→
true
getenv'NANOFIL_STD', gnu89
→
DLoc = Loc
Specs = tspecint
Syn = base
else
sema_errorLoc, 'parameter \'~w\' was not declared, defaults to \'int\'; ISO C99 and later do not support implicit int', N
Prototyped ≡ false
param_typeparamSpecs, dclnone, Syn, none, T0, Env, _
P ≢ T
→
atom_concat'%knr_', N, Fresh
Param = paramPSpecs, dclsomeFresh, base, none
Locals = declDLoc, Specs, initdclsomeN, Syn, none, idFresh
else
Param = paramSpecs, dclsomeN, Syn, none
Locals = []
transparent_union+Type, +Env, -Memberssemidet
Typeina parameter's unqualified type
Envinthe scope
Membersoutthe types of its members, in order

Type is a union with GNU's transparent_union attribute, on its definition or on a typedef of it, that clang keeps transparent: its first member is not floating, and every member has the first's size and alignment. clang otherwise warns and ignores the attribute, and so does nanofil. The parameter keeps the union type, which clang and filcc pass as its first member: a union of one eightbyte has the Fil-C signature of that member, pointer (p) or integer.

GCC manual, Common Type Attributes, transparent_unionClang, SemaDecl.cpp checkTransparentUnion: size, alignment and floating first field
2160transparent_unionunionId, Env, Ms ←
Env = e_, _, Defs, _
get_assoctransparentId, Defs, t
get_assocId, Defs, defunion, Fields, _, _
findallT, memberm_, T, _, Fields, Ms
Ms = First|_
¬float_typeFirst
catchtype_size_alignFirst, Env, Size, Align, bad_, fail
forallmemberM, Ms, catchtype_size_alignM, Env, Size, Align, bad_, fail
transparent_member+Param, +T, +Null, +Env, -Membersemidet
Paramina parameter's unqualified type
Tinthe unqualified value type of an argument for it, not Param
Nullinnull if the argument is a null pointer constant, else value
Envinthe scope
Memberoutthe type of the member the argument initializes

An argument for a transparent union parameter initializes the first member, in order, that takes it: a pointer member takes a void * or a null pointer constant, and any member a value of a compatible type, an arithmetic value for an arithmetic member, or a pointer to a compatible type or from or to void. The argument is converted to that member's type, never reinterpreted as the first member.

GCC manual, Common Type Attributes, transparent_unionClang, SemaExpr.cpp CheckTransparentUnionArgumentConstraints
2185transparent_memberParam, T, N, Env, M ←
transparent_unionParam, Env, Ms
memberM, Ms
member_takesM, T, N, Env
!
2190member_takesptr_, ptrvoid, _, _ ←
!
2191member_takesptr_, _, null, _ ←
!
2192member_takesM, T, _, _ ←
M ≡ T
!
2193member_takesM, T, _, _ ←
2194member_takesptrA, ptrB, _, Env ←
A ≡ void
or
!
union_cast_member+Union, +T, +Env, -Namesemidet
Unioninthe union type a cast names
Tinthe unqualified value type of the cast's operand
Envinthe scope
Nameoutthe first named member of type T

GNU C casts a value to a union one of whose members has its type: the result is the union with that member initialized, a compound literal (mailutils casts a function to its sieve_op_t this way). Without such a member the cast stays refused, as clang refuses it.

GCC manual, Cast to a Union TypeClang, SemaCast.cpp: cast to union type from type not present in union
2209union_cast_memberunionId, T, Env, Name ←
Env = e_, _, Defs, _
get_assocId, Defs, defunion, Fields, _, _
membermsomeName, M, _, Fields
!
parameter_accepts+P, +T, +Null, +Env

an argument of unqualified value type T (and nullness Null) may be passed for a parameter of unqualified type P: assigned to it, or to a transparent union's member.

2218parameter_acceptsP, T, N, Env ←
assignableT, N, P, Env
!
2219parameter_acceptsP, T, N, Env ←
T ≢ P
transparent_memberP, T, N, Env, _
transparent_argument+Param, +XV, +Env, -Membersemidet
Paramina parameter's type
XVinan argument's value tree
Envinthe scope
Memberoutthe member type XV initializes, when Param is a transparent union and XV is not one

As transparent_member/5, for the lowering of a call.

GCC manual, Common Type Attributes, transparent_union
2229transparent_argumentParam0, XV, Env, M ←
unqualifiedParam0, Param
loweredXV, T
T ≢ Param
nullnessXV, Env, N
transparent_memberParam, T, N, Env, M
transparent_typedef+Specs, +Type, +Env0, -Env

a typedef of a union with the transparent_union attribute marks the union so.

2236transparent_typedefSpecs, unionId, eO, T, Defs0, N, eO, T, Defs, N ←
memberattrTokens, Specs
!
put_assoctransparentId, Defs0, t, Defs
2239transparent_typedef_, _, Env, Env
transparent_attr+Tokens

the attribute tokens name transparent_union.

2242transparent_attrTokens ←
is_listTokens
attr_wordTokens, Word
memberchkWord, transparent_union, '__transparent_union__'
!
type_adjusted+Type0, -Type

a parameter declared with Type0 has Type. Every other type is its own adjustment: a default case over the exported type terms, so the cuts stay.

2249type_adjustedarrayT, _, ptrT ←
!
2250type_adjustedfuncR, P, V, ptrfuncR, P, V ←
!
2251type_adjustedT, T
type_name_type+TypeName, -Type, +Env0, -Envdet

Type is the type the type name TypeName (as in a cast or sizeof) denotes; Env is Env0 with any tag it defines.

2258type_name_typetypeSpecs, SyntaxType, Type, Env0, Env ←
specs_typeSpecs, Base, Env0, Env1
apply_declaratorSyntaxType, Base, Type, Env1, Env
arith_size+Elem, +Env, -Sizedet
Eleminthe pointed-to type of a pointer in arithmetic
Envinthe scope
Sizeoutthe bytes one step of the pointer moves

The size of Elem, as type_size_align/4 gives it (and throws). GNU C also lets a pointer to void step by one byte, as clang does with a warning; dash's stackblock() + n relies on it. ISO C requires a complete object type.

C11 6.5.6p2GCC manual, Pointer Arithmetic
examples 2/2 hold
✓⟹S = 1
✓⟹S = 8
2275arith_sizeElem, Env, Size ←
unqualifiedElem, void
→
Size = 1
else
type_size_alignElem, Env, Size, _
vm_type+Tsemidet
Tina type

T is variably modified: a variable length array type, or a type derived from one by pointers, arrays, or a function's return or parameter types.

examples 5/5 hold
✓vm_typeptrarrayint, vlaidn
✓vm_typefuncint, int, ptrarrayint, dyntmpv1, fixed
✓vm_typequalifiedconst, arrayarrayint, vlastar, 2
✓vm_typeptrarrayint, 3⟹fails
✓vm_typearrayint, none⟹fails
2292vm_typequalified_, T ←
2293vm_typeptrT ←
2294vm_typearrayT, N ←
2295vm_typefuncRet, Params, _ ←
or
is_listParams
memberParam, Params
vm_typeParam
!
variable_size+N

array length N is not a constant: unevaluated or evaluated at run time.

2305variable_sizevla_
2306variable_sizedyn_
vla_type+Tsemidet
Tina type

T is a variable length array type: an array whose size is not an integer constant expression, or whose element type has no constant size. A pointer to one is variably modified but has a constant size.

examples 5/5 hold
✓vla_typearrayint, dyntmpv1
✓vla_typearrayarrayint, vlaidn, 3
✓vla_typequalifiedconst, arrayint, vlastar
✓vla_typeptrarrayint, vlaidn⟹fails
✓vla_typearrayint, 3⟹fails
2320vla_typequalified_, T ←
2321vla_typearrayT, N ←
unsupported_type+T, -Format, -Argssemidet
Tina type an expression or declaration names
Formatoutthe diagnostic's format
Argsoutits arguments

nanofil does not support T: it has no values nanofil can hold.

examples 4/4 hold
✓unsupported_typebad'_Complex', '~w is not supported', '_Complex'
✓unsupported_typeldouble, _, _⟹fails
✓unsupported_typeptrbad'_Complex', _, _⟹fails
✓unsupported_typearrayint, vlaidn, _, _⟹fails
2333unsupported_typeT, '~w is not supported', Why ←
unsupported_value_type+Type, -Why

nanofil has no values of Type, for the reason Why.

2338unsupported_value_typebadWhy, Why
2339unsupported_value_typearrayT, _, Why ←
bad_type+T, -Why

bad(Why) is the first bad type inside T, if any (not through pointers, which may point at anything).

2343bad_typebadWhy, Why
2344bad_typearrayT, _, Why ←
bad_typeT, Why
2345bad_typefuncRet, Params, _, Why ←
bad_typeRet, Why
→
true
else
is_listParams
memberParam, Params
bad_typeParam, Why

Sizes

type_size_align+Type, +Env, -Size, -Aligndet

Size and Align are the size and alignment in bytes of Type in Env. Throws bad(Why) for a bad or incomplete type.

2358type_size_alignType, Env, Size, Align ←
size_align_Type, Env, Size0, Align0
→
Size = Size0
Align = Align0
else
throwbadincomplete
2363size_align_qualified_, T, Env, Size, Align ←
type_size_alignT, Env, Size, Align
2365size_align_bool, _, 1, 1
2366size_align_char, _, 1, 1
2367size_align_schar, _, 1, 1
2368size_align_uchar, _, 1, 1
2369size_align_short, _, 2, 2
2370size_align_ushort, _, 2, 2
2371size_align_int, _, 4, 4
2372size_align_uint, _, 4, 4
2373size_align_long, _, 8, 8
2374size_align_ulong, _, 8, 8
2375size_align_llong, _, 8, 8
2376size_align_ullong, _, 8, 8
2377size_align_float, _, 4, 4
2378size_align_double, _, 8, 8
2379size_align_ldouble, _, 16, 16
2380size_align_ptr_, _, 8, 8
2381size_align_atomic_, P, _, P, P
2382size_align_array_, N, _, _, _ ←
throwbad'variable length array'
2385size_align_arrayT, N, Env, Size, Align ←
integerN
type_size_alignT, Env, ElemSize, Align
Size is ElemSize × N
2389size_align_vecE, N, A, Env, Size, A ←
type_size_alignE, Env, ES, _
Size is ES × N
2391size_align_structId, Env, Size, Align ←
su_sizeId, Env, Size, Align
2392size_align_unionId, Env, Size, Align ←
su_sizeId, Env, Size, Align
2393size_align_badWhy, _, _, _ ←
throwbadWhy
su_size+Id, +Env, -Size, -Align

the struct or union Id has Size and Align. Fails when it is incomplete; throws bad(Why) when it is bad.

2397su_sizeId, e_, _, Defs, _, Size, Align ←
get_assocId, Defs, Def
def_size_alignDef, Size, Align
2401def_size_aligndef_, _, Size, Align, Size, Align
2402def_size_alignpartialWhy, _, _, _ ←
throwbadWhy
2404def_size_alignbadWhy, _, _ ←
throwbadWhy
expr_alignment+X, +Env, -Adet
Xinthe elaborated operand of GNU __alignof__ applied to an expression
Envinthe scope
Aoutits alignment in bytes

A named object's alignment is its declaration's, with its _Alignas and aligned requests (qalign), as clang's GetAlignOfExpr takes the declaration's; any other operand's is its type's, an array's its element's. A member, whose alignment clang takes from the field's declaration (aligned, packed), is refused, as is a type type_size_align/4 refuses: both throw bad(Why). So is an operand whose type, or an array level of it, is that of some aligned typedef of the unit, unless its declaration's own request settles it: a type keeps no typedef, so *p of an A *p, A aligned(16), would read 4 where clang gives 16. The operand is not evaluated. glibc's string functions assert __alignof__ (table[0]).

GCC manual, Alignment: __alignof__ of an expressionclang, lib/Sema/SemaExpr.cpp, CheckAlignOfExpr
examples 4/4 hold
✓
expr_alignmentvallong, loadobjptrlong, idp, E, 8
✓
expr_alignmentobjarrayshort, 4, ida, E, 2
✓
bind_ordinaryqalignc, 16, E0, E
expr_alignmentobjchar, idc, E, 16
✓
catchexpr_alignmentobjint, memberobjsustruct, s, ids, x, E, _, badW, true
W ≡ '__alignof__ of a member'
2431expr_alignmentobj_, member_, _, _, _ ←
!
throwbad'__alignof__ of a member'
2432expr_alignmentobj_, bitfield_, _, _, _ ←
!
throwbad'__alignof__ of a member'
2433expr_alignmentobj_, idName, Env, A ←
lookup_ordinaryEnv, qalignName, A0
integerA0
!
A = A0
2436expr_alignmentX, Env, A ←
Env = e_, _, Defs, _
get_assocaligned_typedefL, Defs, _
→
throwbad'__alignof__ of an expression whose type may be an aligned typedef'
else
true
type_size_alignT, Env, _, A
array_level+T, -L

L is T or, for an array, one of its element types.

2447array_levelT, T
2448array_levelarrayT, _, L ←
complex_part_node+XS, +Part, +Env, -X

X is GNU __real__ or __imag__ (Part 'builtin.re' or 'builtin.im') of the elaborated XS: the member of a complex XS, an lvalue if XS is; and of a real XS, XS itself for __real__. (GCC's __imag__ of a real value, its zero, is refused.)

2454complex_part_nodeXS, Part, Env, X ←
tree_typeXS, T0
→
member_typeT0, Part, Env, T
member_nodeXS, Part, T, X
Part ≡ 'builtin.re'
→
X = XS
2462complex_part_faultOp, E, Env, F, As ←
untypedE, Env
→
fault_E, Env, F, As
else
F = '~w of a value that is not complex'
As = Op
sizeof_size+Type, +Env, -Sizedet
Typeinthe type of sizeof's operand
Envinthe scope
Sizeoutthe operand's size in bytes

GNU C gives a function type and void the size 1 for sizeof, as for pointer arithmetic on void * and function pointers; clang accepts it with a -Wpointer-arith warning. curl's typecheck-gcc.h applies sizeof to a callback's function designator. The types stay incomplete everywhere else. Other types are as type_size_align/4, which throws bad(Why).

C11 6.5.3.4p1 (the constraint GNU relaxes)GCC manual, Pointer Arithmetic: sizeof is also allowed on void and on function types, and returns 1
examples 2/2 hold
✓sizeof_sizefuncint, [], fixed, _, 1
✓sizeof_sizequalifiedconst, void, _, 1
2482sizeof_sizeType, Env, Size ←
T ≡ void
or
T = func_, _, _
→
Size = 1
else
type_size_alignType, Env, Size, _
complete_type+Type, +Envsemidet

Type is complete in Env: it has a size.

2493complete_typeType, Env ←
catchtype_size_alignType, Env, _, _, bad_, fail
member_type_offset+SU, +Name, +Env, -Type, -Offsetsemidet

The struct or union type SU has a member Name of Type at Offset, looking into anonymous members. Fails if it has none, if Name is a bit-field, or if SU is not defined.

2500member_type_offsetSU, Name, Env, Type, Off ←
member_layoutSU, Name, Env, Type, Off, none
member_layout+SU, +Name, +Env, -Type, -Off, -Bitssemidet
SUinan unqualified struct or union type
Nameina member name
Envinthe scope that defines SU
Typeoutthe member's type
Offoutthe byte offset of the member, or of the Type that holds a bit-field
Bitsoutnone, or bits(Pos, Width) for a bit-field of Width bits from bit Pos

Looks into anonymous members. A bit-field follows the bits before it unless it would cross a boundary of its type's size; a member that is not a bit-field starts at the next byte its alignment allows.

C11 6.7.2.1p11SysV x86-64 psABI 3.1.2, Bit-Fields
examples 4/4 hold
✓
specs_typesustruct, none, fieldtspecchar, dclsomec, base, none, none, fieldtspecint, dclsomea, base, none, num3, none−dec, fieldtspecint, dclsomeb, base, none, num30, none−dec, [], S, E0, E
member_layoutS, a, E, T, Off, Bits
⟹T = int, Off = 0, Bits = bits8, 3
✓
specs_typesustruct, none, fieldtspecchar, dclsomec, base, none, none, fieldtspecint, dclsomea, base, none, num3, none−dec, fieldtspecint, dclsomeb, base, none, num30, none−dec, [], S, E0, E
member_layoutS, b, E, T, Off, Bits
⟹T = int, Off = 4, Bits = bits0, 30
✓
specs_typesustruct, none, fieldtspecint, dclnone, base, none, num8, none−dec, fieldtspecchar, dclsomec, base, none, none, [], S, E0, E
member_layoutS, c, E, T, Off, Bits
type_size_alignS, E, Size, Align
⟹T = char, Off = 1, Bits = none, Size = 2, Align = 1
✓
specs_typesustruct, none, fieldtspecint, dclsomea, base, none, num3, none−dec, [], S, E0, E
member_type_offsetS, a, E, _, _
⟹fails
2544member_layoutSU, Name, Env, Type, Off, Bits ←
SU =.. _, Id
Env = e_, _, Defs, _
get_assocId, Defs, Def
known_membersDef, Members
memberM, Members
named_memberM, Name, Type, Off, Bits
→
true
membermnone, AnonType, AnonOff, Members
member_layoutAnonType, Name, Env, Type, InnerOff, Bits
→
Off is AnonOff + InnerOff
2555named_membermsomeName, Type, Off, Name, Type, Off, none
2556named_membermsomeName, Type, Off, Bits, Name, Type, Off, Bits
2558known_membersdef_, Members, _, _, Members
2559known_memberspartial_, Members, Members

Integers

int_type+Typesemidet
int_type-Typemulti
Typeina type term

Type is an integer type: bool, the character types, and the standard signed and unsigned integer types.

examples 4/4 hold
✓int_typebool
✓int_typedouble⟹fails
✓int_typeptrint⟹fails
✓findallT, int_typeT, Ts⟹lengthTs, 12
2574int_typeT ←
int_infoT, _, _
integer_type?Type, ?Size, ?Signedness, ?Ranknondet
Typein/outan integer type
Sizein/outits width in bytes
Signednessin/outsigned or unsigned
Rankin/outits integer conversion rank, from 0 for bool to 5 for long long

C's integer types on x86-64 Linux. Plain char is signed, long is as wide as long long, and the three character types share a rank.

examples 2/2 hold
✓integer_typelong, S, G, R⟹S = 8, G = signed, R = 4
✓findallT, integer_typeT, 8, unsigned, _, Ts⟹Ts = ulong, ullong
integer_type
2587bool1unsigned0
2588char1signed1
2589schar1signed1
2590uchar1unsigned1
2591short2signed2
2592ushort2unsigned2
2593int4signed3
2594uint4unsigned3
2595long8signed4
2596ulong8unsigned4
2597llong8signed5
2598ullong8unsigned5
int_info?Type, ?Size, ?Signednessnondet
Typein/outan integer type
Sizein/outits width in bytes
Signednessin/outsigned or unsigned

integer_type/4 without the rank.

examples 1/1 hold
✓int_infouchar, S, G⟹S = 1, G = unsigned
2607int_infoType, Size, Sign ←
integer_typeType, Size, Sign, _
rank?Type, ?Ranknondet
Typein/outan integer type
Rankin/outits integer conversion rank
examples 1/1 hold
✓rankschar, R⟹R = 1
2614rankType, Rank ←
integer_typeType, _, _, Rank
unsigned_of+Signed, -Unsignedsemidet
Signedina signed integer type
Unsignedoutthe unsigned type of the same rank
examples 3/3 hold
✓unsigned_ofllong, U⟹U = ullong
✓unsigned_ofchar, U⟹U = uchar
✓unsigned_ofuint, _⟹fails
2624unsigned_ofS, U ←
integer_typeS, Size, signed, R
integer_typeU, Size, unsigned, R
scalar_type+Typesemidet
Typeina type term

Type is an integer, floating-point or pointer type: one that has an IR class.

examples 2/2 hold
✓scalar_typeptrchar
✓scalar_typestruct1⟹fails
2635scalar_typeT ←
float_type?Typenondet
Typein/outa real floating type

The real floating types. long double is the x87 80-bit extended format, as in clang for x86-64 (class x): sizeof 16, alignment 16, 64 significand bits, LDBL_MANT_DIG (docs/float.md).

examples 2/2 hold
✓float_typeldouble
✓float_typeint⟹fails
float_typefloatdoubleldouble
arithmetic_type+Typesemidet
Typeina type term

Type is an integer or floating type.

examples 2/2 hold
✓arithmetic_typebool
✓arithmetic_typeptrint⟹fails
2658arithmetic_typeT ←
2659arithmetic_typeT ←
default_promotion+Type, -Promoteddet
Typeinthe type of an argument with no parameter type
Promotedoutthe type it is passed as

The default argument promotions: float becomes double, and the integer promotions apply to the rest.

examples 3/3 hold
✓default_promotionfloat, P⟹P = double
✓default_promotionchar, P⟹P = int
✓default_promotionptrint, P⟹P = ptrint
2671default_promotionfloat, double ←
!
2672default_promotionT, P ←
integer_promotion+Type, -Promoteddet
Typeinthe type of an operand
Promotedoutits type after the integer promotions

int for an integer type of rank below int, which int can always represent here, otherwise Type itself, also for a type that is not an integer.

examples 4/4 hold
✓integer_promotionushort, P⟹P = int
✓integer_promotionbool, P⟹P = int
✓integer_promotionuint, P⟹P = uint
✓integer_promotiondouble, P⟹P = double
2686integer_promotionT, P ←
rankT, R
R < 3
→
P = int
else
P = T
usual_arith_conversion+TypeA, +TypeB, -Typesemidet
TypeAinthe type of one arithmetic operand
TypeBinthe type of the other
Typeouttheir common real type

The usual arithmetic conversions. A floating operand wins; otherwise both are promoted, and when the signedness differs the unsigned type wins unless the signed one is wider. A signed type exactly as wide as a higher-ranked unsigned one gives way to its own unsigned type.

examples 10/10 hold
✓usual_arith_conversionint, double, T⟹T = double
✓usual_arith_conversiondouble, ldouble, T⟹T = ldouble
✓usual_arith_conversionldouble, float, T⟹T = ldouble
✓usual_arith_conversionfloat, long, T⟹T = float
✓usual_arith_conversionchar, short, T⟹T = int
✓usual_arith_conversionint, uint, T⟹T = uint
✓usual_arith_conversionuint, long, T⟹T = long
✓usual_arith_conversionlong, ullong, T⟹T = ullong
✓usual_arith_conversionllong, ulong, T⟹T = ullong
✓usual_arith_conversionptrint, int, _⟹fails
2711usual_arith_conversionTypeA0, TypeB0, Type ←
TypeA0 ≡ ldouble
or
TypeB0 ≡ ldouble
!
Type = ldouble
2715usual_arith_conversionTypeA0, TypeB0, Type ←
TypeA0 ≡ double
or
TypeB0 ≡ double
!
Type = double
2719usual_arith_conversionTypeA0, TypeB0, float ←
TypeA0 ≡ float
or
TypeB0 ≡ float
!
2722usual_arith_conversionTypeA0, TypeB0, Type ←
int_infoA, SizeA, SignA
int_infoB, SizeB, SignB
rankA, RankA
rankB, RankB
A ≡ B
→
Type = A
SignA ≡ SignB
→
RankA ≥ RankB
→
Type = A
else
Type = B
SignA ≡ unsigned
RankA ≥ RankB
→
Type = A
SignB ≡ unsigned
RankB ≥ RankA
→
Type = B
SignA ≡ signed
SizeA > SizeB
→
Type = A
SignB ≡ signed
SizeB > SizeA
→
Type = B
SignA ≡ signed
→
else
convert_int+V, +Type, -Wsemidet
Vinan integer
Typeinan integer type
WoutV converted to Type

Conversion wraps modulo the width, as the standard requires for unsigned types and GCC and clang define for signed ones; bool is 0 or 1. Fails when Type is not an integer type. The cut commits to bool, which int_info/3 also describes as a byte.

examples 5/5 hold
✓convert_int300, uchar, W⟹W = 44
✓convert_int256, bool, W⟹W = 1
✓convert_int-1, uint, W⟹W = 4294967295
✓convert_int128, schar, W⟹W = -128
✓convert_int1, double, _⟹fails
2755convert_intV, bool, W ←
!
V =:= 0
→
W = 0
else
W = 1
2756convert_intV, Type, W ←
int_infoType, Size, Sign
M is 1 << 8 × Size
U is V mod M
signed_wordSign, U, M, W
signed_word+Signedness, +U, +M, -Wdet
Signednessinsigned or unsigned
Uina word, 0 =< U < M
Minthe modulus of the word
Woutthe value of U read with Signedness
examples 2/2 hold
✓signed_wordsigned, 255, 256, W⟹W = -1
✓signed_wordunsigned, 255, 256, W⟹W = 255
2770signed_wordsigned, U, M, W ←
U ≥ M >> 1
→
W is U−M
else
W = U
2771signed_wordunsigned, U, _, U
type_class?Type, ?Classnondet
Typein/outa scalar type
Classin/outits IR class

w for an integer type of at most 4 bytes, l for an 8-byte one, p for a pointer, s and d for float and double, x for long double. Deterministic when Type is bound.

examples 6/6 hold
✓type_classushort, K⟹K = w
✓type_classullong, K⟹K = l
✓type_classptrvoid, K⟹K = p
✓type_classfloat, K⟹K = s
✓type_classldouble, K⟹K = x
✓type_classstruct0, _⟹fails
2786type_classT, K ←
integer_typeT, Size, _, _
Size =:= 8
→
K = l
else
K = w
2787type_classfloat, s
2788type_classdouble, d
2789type_classldouble, x
2790type_classptr_, p

Arithmetic constant expressions

A constant expression has a type and a value: an integer, IEEE bits for float/double, or ub where evaluating it is undefined (integer division by zero). Operators propagate ub, except from operands that && || and ?: do not evaluate (C11 6.6p3): 1 || 1/0 is 1, and 1/0 alone is not constant. Every operand must still be a constant expression.

The environment is a c_types environment, or pp for a #if expression, where every integer type acts as intmax_t or uintmax_t (6.10.1p4): a constant is a long or ulong, and so is the result of ! == && and the like. The preprocessor has replaced identifiers by 0.

const_value+Expr, +Env, -Value, -Typedet

Expr is an integer constant expression with Value of Type in Env. Throws not_constant when Expr is not one.

2809const_valueExpr, Env, V, T ←
constant_valueExpr, Env, V, T
→
true
else
thrownot_constant
constant_value+Expr, +Env, -Value, -Typedet
Exprinan arithmetic constant expression
Envinthe scope
Valueoutits value; for a floating Type, its unsigned IEEE bits, never a host float
Typeoutits type

As const_value/4, but Type may be a floating type. Throws not_constant when Expr is not a constant expression.

2821constant_valueExpr, Env, V, T ←
const_evalExpr, Env, V0, T0
integerV0
→
V = V0
T = T0
else
thrownot_constant
float_literal+Chars, -Bits, -Typedet
Charsina floating constant's characters, as the lexer gives them
Bitsoutthe IEEE bits of its value, correctly rounded (ieee.pl)
Typeoutfloat for an f or F suffix, ldouble for l or L, else double
examples 1/1 hold
✓float_literal'1', '.', '5', f, B, T⟹B, T = 1069547520, float
2833float_literalCs, Bits, Type ←
appendBody, Suffix, Cs
memberchkSuffix, f, 'F'
→
Type = float
Digits = Body
memberchkSuffix, l, 'L'
→
Type = ldouble
Digits = Body
else
Type = double
Digits = Cs
type_classType, K
ieee:ieee_parseK, Digits, Bits
float_builtin?Name, ?Type, ?Bitsnondet
Namein/outa GNU builtin that names a float constant
Typein/outfloat, double or ldouble
Bitsin/outthe IEEE bits of its value

glibc's HUGE_VAL and INFINITY are builtin constant expressions, not libm calls: __builtin_huge_val and __builtin_inf with their f and l forms, and the NaNs of nan_builtin/3. The value bits stay exact, including in static storage.

GCC manual, Other Built-in Functions
examples 1/1 hold
✓float_builtin'__builtin_huge_valf', T, V⟹T, V = float, 2139095040
2852float_builtin'__builtin_huge_val', double, 0x7ff0000000000000
2853float_builtin'__builtin_inf', double, 0x7ff0000000000000
2854float_builtin'__builtin_huge_valf', float, 0x7f800000
2855float_builtin'__builtin_inff', float, 0x7f800000
2856float_builtin'__builtin_huge_vall', ldouble, 0x7fff8000000000000000
2857float_builtin'__builtin_infl', ldouble, 0x7fff8000000000000000
2858float_builtinName, T, V ←
nan_builtinName, T, V
nan_builtin?Name, ?Type, ?Bitsnondet
Namein/outa GNU NaN builtin
Typein/outfloat, double or long double
Bitsin/outthe IEEE bits of its value (x87's 80 for long double)

__builtin_nan(""), __builtin_nanf("") and __builtin_nanl(""), glibc's NAN, are constant expressions: the quiet NaN of Type with a zero payload, as nan("") is. Only the empty string is accepted; a payload string would name other bits.

GCC manual, Other Built-in FunctionsC11 7.12.11.2p2
examples 2/2 hold
✓nan_builtin'__builtin_nanf', T, V⟹T = float, V = 2143289344
✓nan_builtin'__builtin_inff', _, _⟹fails
nan_builtin
2873'__builtin_nan'double0x7ff8000000000000
2874'__builtin_nanf'float0x7fc00000
2875'__builtin_nanl'ldouble0x7fffc000000000000000
const_convert+V, +From, +To, ?Wsemidet
Vina constant's value: an integer, float bits, or ub
Frominits arithmetic type
Toinan arithmetic type
Win/outV converted to To

An undefined float-to-int conversion gives ub, like integer division by zero, and ub stays ub. W may be bound: a null pointer constant is asked for the value 0, and ub is then not 0.

examples 7/7 hold
✓const_convert300, int, uchar, W⟹W = 44
✓const_convert-1, int, int, W⟹W = -1
✓const_convert2, int, bool, W⟹W = 1
✓const_convert1092616192, float, int, W⟹W = 10
✓const_convert2139095040, float, int, W⟹W ≡ ub
✓const_convertub, int, llong, W⟹W ≡ ub
✓const_convertub, int, llong, 0⟹fails
2896const_convertV0, _, _, V ←
V0 ≡ ub
!
V = ub
2897const_convertV0, T0, T, V ←
T0 ≡ T
!
V = V0
2898const_convertV, From, bool, W ←
!
const_truthV, From, W
2899const_convertV, From, To, W ←
→
type_classFrom, F
ieee:ieee_convertF, K, V, W
→
type_classFrom, F
ieee:ieee_to_intF, V, I
int_infoTo, Size, Sign
Bits is 8 × Size
integer_rangeSign, Bits, Lo, Hi
I ≥ Lo
I ≤ Hi
→
W = I
else
W = ub
2914integer_rangesigned, Bits, Lo, Hi ←
Lo is −1 << Bits−1
Hi is −Lo−1
2915integer_rangeunsigned, Bits, 0, Hi ←
Hi is 1 << Bits−1
2917const_truthub, _, ub ←
!
2918const_truthV, T, W ←
→
ieee:ieee_compareK, ne, V, 0, W
else
truthV ≠ 0, W
pp_value+Expr, -Valuesemidet

Value is the value of the #if expression Expr; fails when Expr is not constant.

2927pp_valueExpr, V ←
const_evalExpr, pp, V, _
integerV
const_eval+Expr, +Env, -V, -Tsemidet

Expr has value V, an integer, float bits or ub, of unqualified type T in Env: its typed tree, used for its value, folds to V. Fails when Expr is not a constant expression.

2936const_evalExpr, Env, V, T ←
elaborateExpr, Env, X
valueX, XV
loweredXV, T
foldedXV, Env, V
folded+XV, +Env, -Vsemidet
XVina value tree
Envinthe scope, or pp in a #if expression
Voutits value: an integer, or float bits

XV is a constant expression of value V. Its types are the tree's: the conversions are those the elaborator chose, so this is arithmetic only. A node that is not a constant (a load, a call, an assignment, an address) does not fold. A comma operator folds, as an extension, only when both operands do: one with an effect on its left, as in the length of int[(c++, 5)], is not a constant, so that array has variable length and the effect happens.

Two addresses built from integer constants differ by a constant number of elements: ghostscript's offset_of, (char *)&((T *)0)->m - (char *)((T *)0), as clang folds it.

examples 6/6 hold
✓
foldedvalint, binadd, valint, int2, valint, int3, E, V
⟹V = 5
✓
foldedvalint, commavalint, int1, valint, int5, E, V
⟹V = 5
✓
foldedvalint, commavalint, incdecpostinc, objint, idc, valint, int5, E, _
⟹fails
✓
foldedvalint, loadobjint, idc, E, _
⟹fails
✓
foldedvallong, binsub, valptrint, castt, valint, int20, valptrint, castt, valint, int4, E, V
⟹V = 4
✓
bind_ordinaryp, varnone, ptrint, E0, E
foldedvallong, binsub, valptrint, loadobjptrint, idp, valptrint, castt, valint, int0, E, _
⟹fails
2972foldedval_, intV, _, V

6.4.4.1, 6.4.4.4

2973foldedvalT, floatCs, _, V ←

6.4.4.2

2974foldedval_, builtinName, [], _, V ←
float_builtinName, _, V
2975foldedval_, builtinName, Args, Env, V ←
object_sizeName, Args, Env, V
2976foldedval_, builtinName, A, Env, V ←
bit_foldedName, A, Env, V
2977foldedval_, unnot, XV, Env, V ←
6.5.3.3p5
operand_valueXV, Env, V0, T0
const_truthV0, T0, B
definedB, V, truthB =:= 0, V
2981foldedvalT0, unOp, XV, Env, V ←
6.5.3.3p2-4
Op ≢ not
operand_valueXV, Env, V0, _
definedV0, V, constant_unaryT, Op, V0, V
2986foldedvalT0, binsub, XA, XB, Env, V ←
6.5.6p9, 6.6p10
loweredXA, ptrE0
loweredXB, ptr_
!
E ≡ void
→
Size = 1
else
catchtype_size_alignE, Env, Size, _, bad_, fail
Size > 0
convert_intA−B//Size, T, V
2996foldedvalT0, binOp, XA, XB, Env, V ←
operand_valueXA, Env, VA, TA
operand_valueXB, Env, VB, TB
binary_kindOp, Kind
kind_binaryKind, Op, VA−TA, VB−TB, T, V
3002foldedvalT0, condXC, XA, XB, Env, V ←
6.5.15p4
operand_valueXC, Env, VC, TC
operand_valueXA, Env, VA, TA
operand_valueXB, Env, VB, TB
const_truthVC, TC, Test
definedTest, V, Test ≠ 0 → const_convertVA, TA, T, V ; const_convertVB, TB, T, V
3011foldedvalT0, cast_, XV, Env, V ←
6.5.4p5
operand_valueXV, Env, V0, From
const_convertV0, From, T, V
3015foldedvalT0, cast_, XV, Env, V ←
6.6p10
const_convertA, ulong, T, V
3019foldedval_, sizeoftypeSpecs, Decl, Env0, V ←
!6.5.3.4p2
type_name_typetypeSpecs, Decl, T, Env0, Env
catchsizeof_sizeT, Env, V, bad_, fail
3022foldedval_, sizeofX, Env, V ←
catchsizeof_sizeT, Env, V, bad_, fail
3025foldedval_, alignofTypeName, Env, V ←
6.5.3.4p3
type_name_typeTypeName, T, Env, Env1
catchtype_size_alignT, Env1, _, V0, bad_, fail
TypeName = typeSpecs, Syntax
inherited_alignmentSpecs, Syntax, Env, alignA
→
V = A
else
V = V0
3033foldedval_, alignof_exprX, Env, V ←
catchexpr_alignmentX, Env, V, bad_, fail
3035foldedval_, offsetofTypeName, Path, Env, V ←
7.19p3
type_name_typeTypeName, T, Env, Env1
offset_pathPath, T, Env1, 0, V
3038foldedval_, commaXA, XV, Env, V ←
foldedXA, Env, _
foldedXV, Env, V
3039foldedval_, loadobjqualifiedQs, _, idName, Env, V ←
6.6p10
memberchkconst, Qs
lookup_ordinaryEnv, cvalName, cV
operand_value+XV, +Env, -V, -T

value tree XV folds to V, of unqualified type T.

3045operand_valueXV, Env, V, T ←
foldedXV, Env, V
loweredXV, T
3047constant_unaryT, Op, A, V ←
→
Op ≡ plus
→
V = A
Op ≡ neg
→
ieee:ieee_negK, A, V
else
fail
else
unary_arithOp, A, R
constant_address+XP, +Env, -Asemidet
XPina pointer value tree
Envinthe scope
Aoutthe address it is, when it is built from an integer constant

An integer constant cast to a pointer, moved by integer constants, and then a member or element of what it points to, is the address A, so a cast of it to an integer folds: offsetof's old spelling (size_t)&((T *)0)->m, and gperf's string pools. clang and GCC fold these in initializers and case labels (6.6p10 allows other forms of constant expression); nothing is dereferenced.

examples 3/3 hold
✓
constant_addressvalptrchar, binadd, valptrchar, castt, valint, int0, valint, int4, E, A
⟹A = 4
✓
constant_addressvalptrint, addrobjint, derefvalptrint, binadd, valptrint, castt, valint, int16, valint, int2, E, A
⟹A = 24
✓
bind_ordinaryp, varnone, ptrint, E0, E
constant_addressvalptrint, castt, objptrint, idp, E, _
⟹fails
3078constant_addressvalptr_, cast_, X, Env, A ←
X = valptr_, _
→
else
operand_valueX, Env, A, From
3082constant_addressvalptrT, binOp, XA, XB, Env, A ←
memberchkOp−S, add−1, sub−-1
constant_addressXA, Env, A0
XI = XB
or
Op ≡ add
constant_addressXB, Env, A0
XI = XA
operand_valueXI, Env, I, IT
T1 ≡ void
→
Size = 1
else
catchtype_size_alignT1, Env, Size, _, bad_, fail
A is A0 + S × I × Size
3093constant_addressvalptr_, addrL, Env, A ←
3094constant_addressvalptr_, decayL, Env, A ←
3096lvalue_addressobj_, derefXP, Env, A ←
3097lvalue_addressobj_, memberL, F, Env, A ←
L = objSU0, _
lvalue_addressL, Env, A0
catchmember_type_offsetSU, F, Env, _, Off, bad_, fail
A is A0 + Off
offset_path+Path, +Type, +Env, +Off0, -Off

offsetof's member path.

3104offset_path[], _, _, Off, Off
3105offset_pathfieldF|Path, T, Env, Off0, Off ←
member_type_offsetT, F, Env, FieldType, FieldOff
Off1 is Off0 + FieldOff
offset_pathPath, FieldType, Env, Off1, Off
3109offset_pathindexX|Path, arrayElemType, _, Env, Off0, Off ←
const_evalX, Env, I, _
integerI
type_size_alignElemType, Env, ElemSize, _
Off1 is Off0 + I × ElemSize
offset_pathPath, ElemType, Env, Off1, Off
env_int_type+Env, +T0, -T

a constant or operator result of type T0 has type T in Env: T0 itself, or in a #if expression intmax_t or uintmax_t (6.10.1p4). A #if expression has no floating constants (6.10.1p1), so a float type has none there.

3120env_int_typee_, _, _, _, T, T
3121env_int_typepp, T0, T ←
int_infoT0, _, Sign
3125intmax_typesigned, long
3126intmax_typeunsigned, ulong
defined+Values, -V, :Goal

V is ub if one of Values is, and otherwise as Goal gives it.

3130definedVs, V, Goal ←
memberchkub, Vs
→
V = ub
else
callGoal
int_value+V0, +T, -V

V is the value V0 converted to T.

3134int_valueV0, T, V ←
definedV0, V, convert_intV0, T, V
truth:Goal, -B

B is 1 if Goal holds, else 0.

3137truthGoal, B ←
callGoal
→
B = 1
else
B = 0
unary_arith?Op, ?X, ?R

the unary arithmetic operator Op makes the arithmetic expression R of its operand X.

unary_arith
3141negX−X
3142plusXX
3143bnotX\X
binary_kind?Op, ?Kind

the binary operator Op converts its operands as Kind (arith, shift, compare or logical) names.

3147binary_kindOp, Kind ←
c_operator_, _, Op, Kind0
Kind0 = arith_
→
Kind = arith
else
Kind = Kind0
kind_binary+Kind, +Op, +A, +B, +T, -Vsemidet
Kindinarith, shift, compare or logical, as binary_kind/2 names Op
Opina binary operator
Ainthe left operand, Value-Type
Binthe right operand, Value-Type
Tinthe type of the result
Voutthe value of the result, or ub

Arithmetic converts both operands to T, a comparison both to their common type (binary_operands/6), and a shift only the left one, to T. A shift count that is negative or not below the width of T is undefined, so it is ub, as is a division by zero or a quotient that the signed type cannot represent. Other signed overflow wraps, as clang and GCC fold it (with a warning).

examples 15/15 hold
✓kind_binaryarith, sub, 1−uint, 2−int, uint, V⟹V = 4294967295
✓kind_binaryarith, div, 1−int, 0−int, int, V⟹V ≡ ub
✓kind_binaryarith, div, -2147483648−int, -1−int, int, V⟹V ≡ ub
✓kind_binaryarith, mod, -2147483648−int, -1−int, int, V⟹V ≡ ub
✓kind_binaryarith, div, -2147483647−int, -1−int, int, V⟹V = 2147483647
✓kind_binaryarith, div, -2147483648−int, -1−long, long, V⟹V = 2147483648
✓kind_binaryarith, add, 2147483647−int, 1−int, int, V⟹V = -2147483648
✓kind_binaryshift, shl, 1−int, 30−int, int, V⟹V = 1073741824
✓kind_binaryshift, shl, 1−int, 32−int, int, V⟹V ≡ ub
✓kind_binaryshift, shl, 1−long, 32−int, long, V⟹V = 4294967296
✓kind_binaryshift, shl, 1−int, 40−long, int, V⟹V ≡ ub
✓kind_binaryshift, shr, 8−int, -1−int, int, V⟹V ≡ ub
✓kind_binaryshift, shl, 1−int, 18446744073709551615−ullong, int, V⟹V ≡ ub
✓kind_binarycompare, lt, -1−int, 0−uint, int, V⟹V = 0
✓kind_binarylogical, land, 0−int, ub−int, int, V⟹V = 0
3189kind_binaryarith, Op, VA−TA, VB−TB, T, V ←
definedVA, VB, V, const_convertVA, TA, T, A, const_convertVB, TB, T, B, float_typeT → type_classT, K, memberchkOp, add, sub, mul, div, ieee:ieee_binaryK, Op, A, B, V ; quotient_overflowsOp, A, B, T → V = ub ; const_arithOp, A, B, R, int_valueR, T, V
3200kind_binaryshift, Op, VA−_, VB−_, T, V ←
definedVA, VB, V, int_infoT, Size, _, VB ≥ 0, VB < 8 × Size → convert_intVA, T, A, const_shiftOp, A, VB, R, convert_intR, T, V ; V = ub
3208kind_binarycompare, Op, VA−TA, VB−TB, _, V ←
binary_operandsOp, TA, TB, CT, CT, _
definedVA, VB, V, const_convertVA, TA, CT, A, const_convertVB, TB, CT, B, float_typeCT → type_classCT, K, ieee:ieee_compareK, Op, A, B, V ; truthconst_compareOp, A, B, V
3218kind_binarylogical, Op, VA−TA, VB−TB, _, V ←
const_truthVA, TA, A
const_truthVB, TB, B
definedA, V, const_logicalOp, A, B, V
3222const_shiftshl, A, B, R ←
R is A << B
3223const_shiftshr, A, B, R ←
R is A >> B
const_logical+Op, +A, +VB, -V

the right operand VB counts only when the left A does not decide.

3227const_logicalland, A, VB, V ←
A =:= 0
→
V = 0
else
definedVB, V, truthVB ≠ 0, V
3229const_logicallor, A, VB, V ←
A ≠ 0
→
V = 1
else
definedVB, V, truthVB ≠ 0, V
quotient_overflows+Op, +A, +B, +T

A / B of the signed type T is not representable, so A / B and A % B are undefined (C11 6.5.5p6): the minimum divided by -1.

3235quotient_overflowsOp, A, -1, T ←
memberchkOp, div, mod
int_infoT, Size, signed
A =:= −1 << 8 × Size−1

Division by zero is undefined: R is ub.

3241const_arithadd, A, B, R ←
R is A + B
3242const_arithsub, A, B, R ←
R is A−B
3243const_arithmul, A, B, R ←
R is A × B
3244const_arithdiv, A, B, R ←
B =:= 0
→
R = ub
else
R is A//B
3245const_arithmod, A, B, R ←
B =:= 0
→
R = ub
else
R is A rem B
3246const_arithand, A, B, R ←
R is A /\ B
3247const_arithor, A, B, R ←
R is A \/ B
3248const_arithxor, A, B, R ←
R is A xor B
3250const_compareeq, A, B ←
A =:= B
3251const_comparene, A, B ←
A ≠ B
3252const_comparelt, A, B ←
A < B
3253const_comparegt, A, B ←
A > B
3254const_comparele, A, B ←
A ≤ B
3255const_comparege, A, B ←
A ≥ B
num_type+V, +Suffix−Radix, -Typedet

Type is the type of an integer constant of value V: the first type in the C11 list for its suffix and radix that can represent it, or ullong when none can.

3262num_typeV, Suffix−Radix, Type ←
constant_typesRadix, Suffix, Types
memberType, Types
fitsV, Type
→
true
else
Type = ullong
constant_types
3270decnoneint, long, llong
3271decuuint, ulong, ullong
3272decllong, llong
3273decululong, ullong
3274decllllong
3275decullullong
3276othernoneint, uint, long, ulong, llong, ullong
3277otheruuint, ulong, ullong
3278otherllong, ulong, llong, ullong
3279otherululong, ullong
3280otherllllong, ullong
3281otherullullong
fits+V, +Type

the non-negative constant V is below Type's maximum.

3284fitsV, Type ←
int_infoType, Size, Sign
Bits is 8 × Size
below_maxSign, V, Bits
below_max+Signedness, +V, +Bits

V is below the maximum of a Bits-wide integer with Signedness.

3291below_maxsigned, V, Bits ←
V < 1 << Bits−1
3292below_maxunsigned, V, Bits ←
V < 1 << Bits

Compatible and composite types

compatible_type+T, +Usemidet
Tina type
Uinanother type

T and U are compatible: the same type, or alike in their parts where one leaves a part open, an array's size or a function's parameters. Integer types of the same width are still distinct, and a qualified type is compatible only with the same qualified type.

examples 3/3 hold
✓compatible_typearrayint, none, arrayint, 3
✓compatible_typelong, llong⟹fails
✓compatible_typeptrint, ptrqualifiedconst, int⟹fails
3309compatible_typeT, U ←
compatible_type+T, +U, +Env

as compatible_type/2, where a transparent union parameter is also compatible with its members (composite_param/4).

3313compatible_typeT, U, Env ←
composite_typeT, U, _, Env
composite_type+T, +U, -Csemidet
Tina type
Uina type compatible with T
Couttheir composite type

The type that says what both say: an array's size where either knows it (a constant one over a variable one), a function's parameters where either has a prototype. Fails when T and U are not compatible. Variable lengths are compatible with any length (6.7.6.2p6).

A parameter's own qualifiers do not count (6.7.6.3p15).

An enum is compatible with its underlying integer type, but not with another enum (6.7.2.2p4): reflection marks each enum's type enum(Tag) (enum_key/3), which code generation drops.

A use of an enum before its definition, as a function's return type, has the placeholder uint (enum_type/5) and is the same type as a later use, whatever its definition's underlying type.

examples 13/13 hold
✓composite_typeptrarrayint, none, ptrarrayint, 3, C⟹C = ptrarrayint, 3
✓composite_typeptrarrayint, vlaidn, ptrarrayint, 3, C⟹C = ptrarrayint, 3
✓composite_typearrayint, 3, arrayint, dyntmpv1, C⟹C = arrayint, 3
✓composite_typefuncint, unspecified, fixed, funcint, ptrchar, fixed, C⟹C = funcint, ptrchar, fixed
✓composite_typefuncint, qualifiedconst, int, fixed, funcint, int, fixed, C⟹C = funcint, int, fixed
✓composite_typefuncint, ptrqualifiedconst, int, fixed, funcint, ptrint, fixed, _⟹fails
✓compatible_typequalifiedenum'E', uint, uint
✓compatible_typequalifiedenum'E', uint, qualifiedenum'G', uint⟹fails
✓composite_typequalifiedenum'E', uint, qualifiedenum'E', int, C⟹C ≡ int
✓composite_typequalifiedenum'E', long, qualifiedenum'E', uint, C⟹C ≡ long
✓composite_typequalifiedenum'E', int, qualifiedenum'E', long, _⟹fails
✓composite_typearrayint, 2, arrayint, 3, _⟹fails
✓composite_typefuncint, [], fixed, funcint, [], variadic, _⟹fails
3356composite_typeT, U, C ←
composite_typeT, U, C, none
composite_type+T, +U, -C, +Envsemidet
Tina type
Uina type compatible with T
Couttheir composite type
Envinthe scope, or none

As composite_type/3 (compatible_type/2). With an Env, not none, a transparent union parameter also agrees with each of its members' types (composite_param/4).

examples 2/2 hold
✓composite_typearrayint, none, arrayint, 3, C, none⟹C ≡ arrayint, 3
✓composite_typeptrfuncint, unspecified, fixed, ptrfuncint, int, fixed, C, none⟹C ≡ ptrfuncint, int, fixed
3371composite_typeT, U, T, _ ←
T ≡ U
!
3372composite_typeT, U, C, Env ←
enum_markedT, K, T0
!
enum_markedU, L, U0
→
K ≡ L
enum_completedT0, U0, T1, U1
else
T1 = T0
U1 = U
composite_typeT1, U1, C, Env
3377composite_typeT, U, C, Env ←
enum_markedU, _, U1
!
composite_typeT, U1, C, Env
3378composite_typequalifiedQs, T, qualifiedQs, U, qualifiedQs, C, Env ←
composite_typeT, U, C, Env
3380composite_typeptrT, ptrU, ptrC, Env ←
composite_typeT, U, C, Env
3381composite_typearrayT, N, arrayU, M, arrayC, K, Env ←
composite_typeT, U, C, Env
3384composite_typefuncR, Ps, V, funcS, Qs, V, funcC, Cs, V, Env ←
composite_typeR, S, C, Env
composite_paramsPs, Qs, Cs, Env
enum_completed+T0, +U0, -T, -U

the underlying types of two uses of one enum, T0 and U0, are T and U, with a use before the enum's definition (GNU's forward reference, as glibc's nss_files.h declares functions returning enum nss_status before nss.h defines it), whose placeholder underlying type is uint (enum_type/5), taken as the definition's: both name the one enum type, which nothing could use before it was complete.

3395enum_completeduint, U, U, U ←
!
3396enum_completedT, uint, T, T ←
!
3397enum_completedT, U, T, U
enum_marked+T, -K, -T1

reflected type T is enum K (enum_key/3), otherwise T1.

3401enum_markedqualifiedQs, X, K, T1 ←
selectenumK, Qs, Rest
!
qualifyRest, X, T1
composite_size+N, +M, -K

array sizes N and M, each none if unknown, agree on K.

3406composite_sizenone, M, M ←
!
3407composite_sizeN, none, N ←
!
3408composite_sizeN, M, M ←
3409composite_sizeN, M, N ←
3410composite_sizeN, N, N
composite_params+Ps, +Qs, -Cs

parameter lists Ps and Qs, each unspecified without a prototype, agree on Cs.

3414composite_paramsunspecified, Qs, Qs, _ ←
!
3415composite_paramsPs, unspecified, Ps, _ ←
!
3416composite_paramsPs, Qs, Cs, Env ←
maplistcomposite_paramEnv, Ps, Qs, Cs

A transparent union parameter agrees with a member's type, and the composite is that member's type, as clang's mergeTransparentUnionType. Members are stored unqualified, so the other parameter is compared without its qualifiers (it is a reflected type when c_lower compares qualified redeclarations).

3423composite_paramEnv, P, Q, C ←
P = qualified_, P1
→
true
else
P1 = P
Q = qualified_, Q1
→
true
else
Q1 = Q
composite_typeP1, Q1, C0, Env
→
C = C0
Env ≢ none
→
Other = Q1
→
Other = P1
unqualifiedOther, Other1
memberM, Ms
composite_typeM, Other1, C0, Env
→
C = C0
unqualified+T0, -Tdet
T0ina reflected type
ToutT0 without qualifiers, at any depth

The type code generation uses: qualifiers say what a program may do with an object, not how it is stored.

examples 2/2 hold
✓unqualifiedptrqualifiedconst, char, T⟹T = ptrchar
✓unqualifiedfuncqualified(volatile), int, unspecified, fixed, T⟹T = funcint, unspecified, fixed
3445unqualifiedT, T ←
varT
!
3446unqualifiedqualified_, T0, T ←
3447unqualifiedptrT0, ptrT ←
3448unqualifiedarrayT0, N, arrayT, N ←
3449unqualifiedfuncR0, Ps0, V, funcR, Ps, V ←
!
Ps0 ≡ unspecified
→
Ps = Ps0
else
maplistunqualified, Ps0, Ps
3452unqualifiedT, T

The typed tree

An expression elaborated is a tree whose every node carries its (reflected, qualified) type, with its category the node's principal functor:

X ::= obj(T, F)    an lvalue designating an object of type T
    | fun(T, F)    a function designator of function type T
    | val(T, F)    a value of type T, not an lvalue

The conversions C applies implicitly are nodes of their own: where an operand is used for its value, value/2 wraps it in load(X) or decay(X). _Generic is resolved here: its tree is the selected association's. Every operand is elaborated but the arguments of a builtin, a statement expression's block and a compound literal's initializer, which stay as the source has them.

elaborate+E, +Env, -Xsemidet
Eina C expression, as c_parse reads it
Envinthe scope it is read in
Xoutits typed tree

X is E as C reads it: each node typed, and lvalues, function designators and values told apart. Fails when E cannot be typed.

__func__ is a static char array holding the enclosing function's name, so its size counts the name and the null; the lowerer binds enclosing(function) to that name for the function's body.

examples 13/13 hold
✓
bind_ordinaryenclosingfunction, main, E0, E
elaborateid'__func__', E, X
⟹X = objarraychar, 5, predefined'__func__'
✓
bind_ordinaryx, varnone, int, E0, E
elaborateunaddr, idx, E, X
⟹X = valptrint, addrobjint, idx
✓
elaboratenum3000000000, none−dec, E, X
⟹X = vallong, int3000000000
✓
elaboratenum3000000000, none−other, E, X
⟹X = valuint, int3000000000
✓
bind_ordinarya, varnone, arrayint, 4, E0, E
elaborateindexida, chr1, E, X
⟹X = objint, derefvalptrint, binadd, valptrint, decayobjarrayint, 4, ida, valint, int1
✓
bind_ordinaryx, varnone, short, E0, E
elaboratecommaassign=, idx, chr1, idx, E, X
⟹X = valshort, commavalshort, assign=, objshort, idx, valint, int1, valshort, loadobjshort, idx
✓
elaboratestr104, 105, E, X
⟹X = objarraychar, 3, str104, 105
✓
elaborateunderef, chr1, E, _
⟹fails
✓
bind_ordinaryf, fnfuncint, int, variadic, f, extern, E0, E
elaboratecallidf, chr1, chr2, E, X
⟹X = valint, call_, valint, int1, valint, int2
✓
bind_ordinaryf, fnfuncint, int, fixed, f, extern, E0, E
elaboratecallidf, chr1, chr2, E, _
⟹fails
✓
bind_ordinaryf, fnfuncint, ptrint, fixed, f, extern, E0, E
elaboratecallidf, chr1, E, _
⟹fails
✓
elaborateunaddr, chr1, E, _
⟹fails
✓
bind_ordinaryc, varnone, int, E0, E1
bind_ordinaryqtypec, qualifiedconst, int, E1, E
elaborateassign=, idc, chr1, E, _
⟹fails
3517elaborateE, Env, X ←
elabE, Env, X0
!
X = X0
3519elabidName, Env, X ←
lookup_ordinaryEnv, Name, Binding
!
identifier_typeBinding, Name, Env, T
binding_nodeBinding, T, Name, X
3523elabidName, Env, objarraychar, N, predefinedName ←
6.4.2.2
memberchkName, '__func__', '__FUNCTION__', '__PRETTY_FUNCTION__'
clang's __PRETTY_FUNCTION__ spells the declarator, which nanofil's
predefined string does not, so its size stays unknown.
Name ≢ '__PRETTY_FUNCTION__'
lookup_ordinaryEnv, enclosingfunction, Func
→
atom_lengthFunc, N0
N is N0 + 1
else
N = none
3531elabnumV0, S, Env, valT, intV ←
num_typeV0, S, T0
env_int_typeEnv, T0, T
convert_intV0, T, V
3533elabchrV, Env, valT, intV ←
env_int_typeEnv, int, T
3534elabfloatCs, Env, valT, floatCs ←
lastCs, Suffix
memberchkSuffix, f, 'F'
→
T0 = float
memberchkSuffix, l, 'L'
→
T0 = ldouble
else
T0 = double
env_int_typeEnv, T0, T
3541elabstrBytes, _, objarraychar, N, strBytes ←
lengthBytes, M
N is M + 1
3543elabwstrT, Values, _, objarrayT, N, wstrT, Values ←
lengthValues, M
N is M + 1
3545elabcastTN, E, Env, X ←
GNU cast to union
type_name_typeTN, T0, Env, Env1
unqualifiedT0, unionId
operandE, Env, _, F0
F ≠ unionId
union_cast_memberunionId, F, Env1, Name
!
elabcomplitTN, listentryfieldName, E, Env, X
3550elabcastTN, E, Env, valT, castTN, XV ←
type_name_typeTN, T0, Env, _
operandE, Env, XV, F0
reflect_typeTN, T0, Env, T
3555elabcomplitTN, Init, Env, objT, complitTN, Init ←
type_name_typeTN, T0, Env, _
reflect_typeTN, T0, Env, T
3557elabunaddr, E, Env, valptrT, addrX ←
6.5.3.2p1
elaborateE, Env, X
X ≠ val_, _
X ≠ obj_, bitfield_, _
3560elabunderef, E, Env, X ←
operandE, Env, XV, ptrT
designatorT, derefXV, X
3563elabunOp, E, Env, valT, incdecOp, X ←
6.5.3.1p1
memberchkOp, preinc, predec, postinc, postdec
elaborateE, Env, X
valueX, XV
3567elabunOp, E, Env, valT, unOp, XV ←
memberchkOp, plus, neg, bnot, not
operandE, Env, XV, T1
unary_operandOp, T1, T0
env_int_typeEnv, T0, T
3571elabindexA, I, Env, X ←
GNU vectors
elaborateA, Env, XS
tree_typeXS, T0
unqualifiedT0, vecE, _, _
!
operandI, Env, XI, TI
XS = obj_, _
→
T0 = qualifiedQ, _
→
T = qualifiedQ, E
else
T = E
X = objT, vlaneXS, XI
else
valueXS, XV
X = valE, vlaneXV, XI
3579elabindexA, I, Env, X ←
6.5.2.1p2
elabunderef, binadd, A, I, Env, X
3581elabmemberE, F, Env, X ←
elaborateE, Env, XS
tree_typeXS, SU
member_typeSU, F, Env, T
member_layoutU, F, Env, _, _, bits_, W6.7.2.1p10
→
XS = obj_, _
X = objT, bitfieldmemberXS, F, W
else
member_nodeXS, F, T, X
3588elabreal_partE, Env, X ←
GNU
elaborateE, Env, XS
complex_part_nodeXS, 'builtin.re', Env, X
3590elabimag_partE, Env, X ←
elaborateE, Env, XS
complex_part_nodeXS, 'builtin.im', Env, X
3592elabimagfloatCs, Env, valT, imagXF ←
GNU 1.0i
elabfloatCs, Env, XF
3594elabarrowE, F, Env, X ←
6.5.2.3p4
elabmemberunderef, E, F, Env, X

filc_test_support.h's zhas_union(p) only blocks optimizations in filcc, which erases the call (FilPizlonator); no library defines it.

3598elabcallidzhas_union, E, _, valvoid, builtinzhas_union, E ←
!
3599elabcallid'__builtin_constant_p', E, Env, X ←
¬lookup_ordinaryEnv, '__builtin_constant_p', _
!
elabnumV, none−dec, Env, X
3603elabcallidName, Args, Env, X ←
¬lookup_ordinaryEnv, Name, _
checking_builtinName, Chk, Plain
!
lookup_ordinaryEnv, Chk, _
→
elabcallidChk, Args, Env, X
else
unchecked_callPlain, Args, Env, Call
lookup_ordinaryEnv, Plain, _
elabCall, Env, X
3610elabcallidName, Args, Env, X ←
¬lookup_ordinaryEnv, Name, _
variable_ordersName, Args, Env, Evaluated, Args1
!
foldlevaluated_first, Evaluated, callidName, Args1, E
elabE, Env, X
3615elabcallidName, Args, Env, X ←
¬lookup_ordinaryEnv, Name, _
atomic_builtinName, Args, Env, R
!
R = exprE
→
elabE, Env, X
else
R = builtinT
X = valT, builtinName, Args
3621elabcallidName, str[], Env, valT, builtinName, [] ←
¬lookup_ordinaryEnv, Name, _
nan_builtinName, T, _
!
3624elabcallidName, Args, Env, valT, builtinName, Args ←
GNU vectors
¬lookup_ordinaryEnv, Name, _
!
vector_builtin_typeName, Args, Env, T
3628elabcallidName, Args, Env, valT, builtinName, Args ←
¬lookup_ordinaryEnv, Name, _
Args ≡ []
float_builtinName, T, _
¬nan_builtinName, _, _
→
true
else
3633elabcallE, Args, Env, valT, callXV, XArgs ←
6.5.2.2p2
operandE, Env, XV, ptrfuncT, Ps, V
maplistargumentEnv, Args, XArgs
arity_fitsPs, V, Args
passesPs, XArgs, Env
3638elabassignOp, A, B, Env, valT, assignOp, X, XB ←
6.5.16p2
elaborateA, Env, X
argumentEnv, B, XB
loweredXB, TB
assignsOp, TA, XB, TB, Env
3643elabcommaA, B, Env, valT, commaXA, XV ←
argumentEnv, A, XA
operandB, Env, XV, T
3646elabsizeof_exprE, Env, valulong, sizeofX ←
6.5.3.4p1
elaborateE, Env, X
X ≠ obj_, bitfield_, _
3648elabsizeof_typeTN, _, valulong, sizeofTN
3649elabalignofTN, _, valulong, alignofTN
3650elabalignof_exprE, Env, valulong, alignof_exprX ←
GNU
elaborateE, Env, X
3652elaboffsetofTN, Path, _, valulong, offsetofTN, Path
3653elablabel_addrName, Env, X ←
GNU &&label
label_valueName, Env, E
elabE, Env, X
3655elabbinOp, A, B, Env, valT, binOp, XA, XB ←
operandA, Env, XA, TA
operandB, Env, XB, TB
env_int_typeEnv, T0, T
3658elabcondC, A, B, Env, valT, condXC, XA, XB ←
6.5.15p2
argumentEnv, C, XC
tree_typeXC, TC
armsA, B, Env, XA, XB, T
3661elabgcondC, B, Env, valT, gcondXC, XB ←
armsC, B, Env, XC, XB, T
tree_typeXC, TC
3663elabva_argA, TN, Env, valT, va_argTN, XA ←
argumentEnv, A, XA
type_name_typeTN, T0, Env, _
reflect_typeTN, T0, Env, T
3666elabbit_castTN, E, Env, valT, bit_castXA ←
argumentEnv, E, XA
tree_typeXA, TA
type_name_typeTN, T0, Env, _
bit_castableTA, T, Env
3670elabstmtexpratLoc, blockItems, Env, valT, stmtexpratLoc, blockItems ←
block_value_typeItems, Env, T
3672elabtypes_compatibleTA, TB, Env, X ←
!
elabnumV, none−dec, Env, X
3675elabcallidName, S, P, Env, X ←
memberchkName, '__atomic_always_lock_free', '__c11_atomic_is_lock_free', '__atomic_is_lock_free'
¬lookup_ordinaryEnv, Name, _
catchconst_valueS, Env, Size, _, not_constant, fail
operandP, Env, XP, PT0
lock_freeName, Size, XP, PT, Env, V
!
elabnumV, none−dec, Env, X
3683elabcallid'__builtin_classify_type', E, Env, X ←
¬lookup_ordinaryEnv, '__builtin_classify_type', _
expression_typeE, Env, T0
!
elabnumV, none−dec, Env, X
3688elabcallid'__builtin_choose_expr', C, A, B, Env, X ←
¬lookup_ordinaryEnv, '__builtin_choose_expr', _
!
catchconst_valueC, Env, V, _, not_constant, fail
→
V ≠ 0
→
elabA, Env, X
else
elabB, Env, X
else
thrownanofil_error'__builtin_choose_expr needs a constant condition'
3694elabcallidName, Args, Env, X ←
¬lookup_ordinaryEnv, Name, _
hint_builtinName, Args, E
!
elabcasttypetspecvoid, base, E, Env, X
3698elabgenericC, As, Env, X ←
6.5.1.1
operandC, Env, _, CT0
expression_typeC, Env, T0
enum_markedT0, K, _
→
CT = qualifiedenumK, CT0lvalue conversion keeps it
else
CT = CT0
generic_selectionCT, As, Env, constant, E
elaborateE, Env, X
argument+Env, +E, -XV

XV is E elaborated and used for its value: a call argument, the source of an assignment, a ?: condition, or the left operand of a comma, whose value is discarded after it is evaluated.

3710argumentEnv, E, XV ←
operandE, Env, XV, _
assignment_type+X, -T

an assignment to X has type T, X's own but for a bit-field, whose value has its promoted type (6.5.16p3).

3714assignment_typeobjT0, bitfield_, W, T ←
3716assignment_typeX, T ←
unqualifiedT0, atomicS, _6.5.16p3: lvalue-converted
→
T = S
else
T = T0
lowered+XV, -Tdet
XVina typed expression tree
Toutits type without qualifiers, the type lowering gives it
examples 1/1 hold
✓loweredvalqualifiedconst, int, idx, T⟹T ≡ int
3727loweredXV, T ←
assigns+Op, +TA, +B, +TB, +Env

assignment Op stores B, a value of type TB, into an object of unqualified type TA: B as it is (6.5.16.1), or the result of the operator applied to the two (6.5.16.2).

3732assigns=, TA, XB, TB, Env ←
!
nullnessXB, Env, N
assignableTB, N, TA, Env
3733assignsOp, TA, _, TB, _ ←
or
TA = vec_, _, _
or
binary_operandsBinOp, TA, TB, _, _, T
assignableT, value, TA
arity_fits+Ps, +V, +Args

a function with parameters Ps, fixed or variadic, takes Args.

3740arity_fitsunspecified, _, _ ←
!
3741arity_fitsPs, V, Args ←
lengthPs, NP
lengthArgs, NA
V ≡ fixed
→
NA =:= NP
else
NA ≥ NP
passes+Ps, +XArgs, +Env

each argument, elaborated in XArgs, may be assigned to the unqualified type of its parameter in Ps.

3747passesunspecified, _, _ ←
!
3748passes[], _, _
3749passesP|Ps, X|Xs, Env ←
nullnessX, Env, N
parameter_acceptsTP, T, N, Env
passesPs, Xs, Env
identifier_type+Binding, +Name, +Env, -Tdet
Bindinginwhat the identifier Name stands for in Env
Nameinan ordinary identifier
Envinthe scope, which records Name's reflected type
Toutthe type it has as an expression

An enumeration constant has its enumeration's type. An object or function has the type its declaration reflects, qualifiers included, with array sizes that initializers have since completed.

examples 2/2 hold
✓
bind_ordinaryqtypec, qualifiedconst, int, E0, E
identifier_typevarnone, int, c, E, T
⟹T = qualifiedconst, int
✓
bind_ordinaryqtypes, arraychar, none, E0, E
identifier_typegvararraychar, 6, s, static, none, s, E, T
⟹T = arraychar, 6
3770identifier_typeenumconst_, T0, _, _, T ←
!
T = T0
3771identifier_typeBinding, Name, Env, T ←
binding_typeBinding, T0
lookup_ordinaryEnv, qtypeName, RT
→
else
T = T0
binding_type
3777var_, TT
3778gvarT, _, _, _T
3779fnT, _, _T
3780enumconst_, TT
readonly_object_type+RTsemidet
RTinan object's reflected type (reflect_type/4), qualifiers kept

An object of type RT is const: RT is const-qualified, or an array whose elements are (an array type's qualifiers are its elements'). Modifying it is undefined, so Fil-C gives a static one a read-only capability and filcc traps a store through a cast (clang marks such a global constant, volatile or not). A struct with a const member is not itself const, and C has no mutable members, so a const struct is read-only as a whole.

examples 8/8 hold
✓readonly_object_typequalifiedconst, int
✓readonly_object_typequalifiedconst, (volatile), int
✓readonly_object_typearrayqualifiedconst, int, 3
✓readonly_object_typequalifiedconst, arrayint, 3
✓readonly_object_typeint⟹fails
✓readonly_object_typeptrqualifiedconst, char⟹fails
✓readonly_object_typequalified(volatile), int⟹fails
✓readonly_object_typestruct1⟹fails
3802readonly_object_typequalifiedQs, T ←
memberchkconst, Qs
→
true
3804readonly_object_typearrayT, _ ←
reflected_shape+RT, +T, -S

S is reflected type RT with the array dimensions of declared type T, which an initializer may have completed since RT was recorded.

3809reflected_shapequalifiedQs, R, T, qualifiedQs, S ←
3811reflected_shapearrayR, _, arrayT, N, arrayS, N ←
3813reflected_shapeptrR, ptrT, ptrS ←
vm_typeTevaluated lengths
!
3815reflected_shapeR, _, R
binding_node+Binding, +T, +Name, -X

X is the identifier Name, bound to Binding, of type T.

binding_node
3819var_, _TNameobjT, idName
3820gvar_, _, _, _TNameobjT, idName
3821fn_, _, _TNamefunT, idName
3822enumconstV, _T_valT, intV
designator+T, +F, -X

F, designating something of type T, is a function designator or an lvalue (6.5.3.2p4).

3826designatorT, F, X ←
T = func_, _, _
!
X = funT, F
3827designatorT, F, objT, F
member_node+XS, +F, +T, -X

member F, of type T, of struct or union XS is an lvalue if XS is (6.5.2.3p3).

3831member_nodeobjTS, FS, F, T, objT, memberobjTS, FS, F
3832member_nodevalTS, FS, F, T, valT, membervalTS, FS, F
member_type+SU, +F, +Env, -Tsemidet
SUina struct or union type, perhaps qualified
Fina member name
Envinthe scope that defines SU
Toutthe member's reflected type

Looks into anonymous members. A qualified SU qualifies the member.

examples 3/3 hold
✓
member_typestruct'builtin.va_list', fp_offset, E, T
⟹T = uint
✓
member_typequalifiedconst, struct'builtin.va_list', reg_save_area, E, T
⟹T = qualifiedconst, ptrvoid
✓
member_typestruct'builtin.va_list', gp, E, _
⟹fails
3850member_typequalifiedQs, SU, F, Env, T ←
!
member_typeSU, F, Env, T0
qualifyQs, T0, T
3852member_typeSU, F, Env, T ←
SU =.. _, Id
Env = e_, _, Defs, _
get_assocqmembersId, Defs, Members
!
members_typeMembers, F, Env, T
3856member_typeSU, F, Env, T ←
built-in va_list
member_type_offsetSU, F, Env, T, _
members_type+Members, +F, +Env, -T

one of Members, or a member of an anonymous one, is F of type T.

3861members_typeMembers, F, _, T ←
memberchkqsomeF, T0, Members
!
T = T0
3863members_typeMembers, F, Env, T ←
memberqnone, Anon, Members
member_typeAnon, F, Env, T
!
library_builtin?Name, ?Fnondet
Namein/outa GNU builtin spelling of a standard, POSIX or GNU library function
Fin/outthat function

GCC and clang declare __builtin_F for these library functions F whether or not a header declares F, and a call is a call of F (bison parsers call __builtin_memcpy without <string.h>, as netcdf's dcetab.c does; glibc's own sources call __builtin_mempcpy and __builtin_stpcpy). builtin_declarations/1 declares each with F's prototype, named F in the object.

GCC manual, Other Built-in FunctionsC11 7.24, 7.22.3, 7.22.4POSIX.1-2008 stpcpy
examples 3/3 hold
✓library_builtin'__builtin_memcpy', F⟹F = memcpy
✓library_builtin'__builtin_mempcpy', F⟹F = mempcpy
✓library_builtinmemcpy, _⟹fails
3882library_builtinName, F ←
atom_concat'__builtin_', F, Name
library_proto?F, ?Ret, ?Paramsnondet

Library function F's prototype, with types named as proto_type/3 reads them.

library_proto
3890memcpyvpvp, cvp, size
3891memmovevpvp, cvp, size
3892memsetvpvp, int, size
3893memcmpintcvp, cvp, size
3894memchrvpcvp, int, size
3895mempcpyvpvp, cvp, size
3896strlensizeccp
3897strcmpintccp, ccp
3898strncmpintccp, ccp, size
3899strcpycpcp, ccp
3900strncpycpcp, ccp, size
3901stpcpycpcp, ccp
3902strcatcpcp, ccp
3903strncatcpcp, ccp, size
3904strchrcpccp, int
3905strrchrcpccp, int
3906strstrcpccp, ccp
3907strdupcpccp
3908mallocvpsize
3909callocvpsize, size
3910reallocvpvp, size
3911freevoidvp
3912abortvoid[]
3913absintint
3914labslonglong
proto_type
3916vptspecvoidptrbase, []
3917cvpqualconst, tspecvoidptrbase, []
3918cptspeccharptrbase, []
3919ccpqualconst, tspeccharptrbase, []
3920inttspecintbase
3921longtspeclongbase
3922sizetspecunsigned, tspeclongbase
3923voidtspecvoidbase
builtin_declarations-Itemsdet
Itemsouttop-level declarations of every library_builtin/2 name

Each declares __builtin_F with F's prototype and the asm label F, as if a unit began with void *__builtin_memcpy(void *, const void *, size_t) __asm__("memcpy"); and so on.

GCC manual, Other Built-in Functions
3932builtin_declarationsItems ←
findalldeclbuiltin:0, RSpecs, initdclsomeName, funcRDecl, paramsPs, fixed, asmF, none,
atom_concat'__builtin_', F, Name
proto_typeR, RSpecs, RDecl
findallparamSpecs, dclnone, D, none,
memberA, As
proto_typeA, Specs, D
,
Ps0
Ps0 ≡ []
→
Ps = []
else
Ps = Ps0
,
Items
label_value+Name, +Env, -Esemidet
Nameina label whose address GNU C's &&Name takes
Envinthe scope of the function that defines it, where c_lower bound
Eoutthe expression (void *)N, which &&Name stands for

label_addr(Name) to the label's number N (1, 2, ... in the order the function takes the addresses)

A label's value is a void * that only goto * of the same function uses. nanofil gives it no capability: the address N is the label's number, an address constant (so static tables of labels work), and goto * dispatches on it among the function's address-taken labels, trapping on any other value. Values of labels of different functions may compare equal.

GCC manual, Labels as Values
examples 2/2 hold
✓
bind_ordinarylabel_addrl, 3, E0, E
⟹X = casttypetspecvoid, ptrbase, [], num3, none−dec
✓⟹fails
3962label_valueName, Env, casttypetspecvoid, ptrbase, [], numN, none−dec ←
lookup_ordinaryEnv, label_addrName, N
checking_builtin?Name, ?Chk, ?Plainnondet
Namein/outa GNU object-size checking builtin, which glibc's _FORTIFY_SOURCE headers call
Chkin/outthe library function that checks
Plainin/outthe function it checks

An undeclared Name called is a call of Chk with the same arguments when Chk is declared (glibc declares the printf family's). Else a string or memory function's last argument is the destination's object size, and when it folds to (size_t)-1, unknown, the call is one of Plain without it, as GCC and clang lower it; any other size is refused. Fil-C checks every access anyway.

GCC manual, Object Size Checking
examples 2/2 hold
✓checking_builtin'__builtin___memcpy_chk', C, P⟹C = '__memcpy_chk', P = memcpy
✓checking_builtin'__builtin_memcpy', _, _⟹fails
3980checking_builtinName, Chk, Plain ←
atomName
atom_concat'__builtin___', Rest, Name
atom_concatPlain, '_chk', Rest
atom_concat'__', Rest, Chk
unchecked_call+Plain, +Args, +Env, -Call

Call is Plain of Args but the last, a constant object size of (size_t)-1, for a memory or string function.

3988unchecked_callPlain, Args, Env, callidPlain, Args0 ←
memberchkPlain, memcpy, memmove, mempcpy, memset, strcpy, stpcpy, strncpy, stpncpy, strcat, strncat
appendArgs0, Size, Args
catchconst_valueSize, Env, V, _, not_constant, fail
V =:= 0xffffffffffffffff
object_size+Name, +Args, +Env, -Vsemidet
Namein__builtin_object_size or __builtin_dynamic_object_size
Argsinits pointer and its constant type, 0 to 3
Envinthe scope the type is read in
Voutthe size it answers: (size_t)-1 for types 0 and 1, 0 for 2 and 3

nanofil always answers that the size is unknown, which GCC documents as a correct answer for any pointer; the pointer is not evaluated. Fails unless the type is a constant 0 to 3.

GCC manual, Object Size Checking
examples 3/3 hold
✓
object_size'__builtin_object_size', idp, num1, none−dec, E, V
⟹V = 18446744073709551615
✓
object_size'__builtin_dynamic_object_size', idp, num2, none−dec, E, V
⟹V = 0
✓
object_size'__builtin_object_size', idp, num4, none−dec, E, _
⟹fails
4010object_sizeName, _, Type, Env, V ←
memberchkName, '__builtin_object_size', '__builtin_dynamic_object_size'
catchconst_valueType, Env, K, _, not_constant, fail
memberchkK, 0, 1
→
V = 0xffffffffffffffff
memberchkK, 2, 3
→
V = 0
atomic_builtin+Name, +Args, +Env, -Rsemidet
Nameina GNU __atomic or clang __c11_atomic builtin
Argsinits argument expressions
Envinthe scope
Routexpr(E), the plain C expression E it is, or builtin(T), a builtin call of type T that lowering makes atomic

On an integer, __atomic_load_n(P, Order) is *P and, for a relaxed or release Order, __atomic_store_n(P, V, Order) is (void)(*P = V), both after Order: an aligned integer access is already atomic on x86-64, and none of those orders needs a fence there. A sequentially consistent integer store is the store and then a fence (mov; mfence, where clang emits xchg; both are seq_cst stores against plain-mov seq_cst loads). On a pointer, whose capability is a second word, both are builtin(T) at every order: lowering emits IR's atomic_load and atomic_store, which call Fil-C's helpers as clang does, single-copy atomic and sequentially consistent (docs/m4.md, Atomic builtins). *P is an integer or a pointer, and Order a constant: relaxed, consume, acquire or seq_cst for a load; relaxed, release or seq_cst for a store. Anything else fails (fault/4 says why). A store through a pointer to an _Atomic object is builtin(void) at every order, since assigning the object would be a seq_cst store.

<stdatomic.h>'s generic functions are clang's __c11_atomic builtins, whose pointer must point to an _Atomic object, here of an integer, pointer, float or double. __c11_atomic_load and __c11_atomic_store are __atomic_load_n and __atomic_store_n, with the same orders; __c11_atomic_init(P, V) stores V without atomicity, as clang does. A thread fence of a constant order is builtin(void), a fence only when seq_cst (x86-64 orders the rest itself); a signal fence orders nothing a single thread can observe.

The read-modify-writes are builtin(T) of the type they return, each one sequentially consistent atomic step whatever its orders, which lowering makes a loop of IR's cas (docs/m4.md): __c11_atomic_exchange and __c11_atomic_fetch_OP (add, sub, and, or, xor, nand, max, min) on an _Atomic integer return the old value, add and sub also on an _Atomic pointer, by elements, and exchange also on an _Atomic float or double; __c11_atomic_compare_exchange_strong and _weak, on any of these, return whether *P equaled *Expected, bit for bit (so 0.0 is not -0.0, as clang's cmpxchg of the bits has it), and write *P to *Expected only when not (a weak one never fails spuriously). GCC's __atomic_exchange_n, __atomic_fetch_OP, __atomic_OP_fetch (the new value), __atomic_compare_exchange_n, the __sync builtins and __atomic_test_and_set and __atomic_clear (a byte at any object pointer) work on a plain integer, or a pointer to exchange or compare it; their fetch-and-ops on a pointer are refused (clang adds bytes, not elements, and filcc cannot compile them). GCC's generic __atomic_load(P, R, O), __atomic_store(P, V, O), __atomic_exchange(P, V, R, O) and __atomic_compare_exchange(P, E, D, W, S, F) pass the values through pointers: they are *R = __atomic_load_n(P, O), __atomic_store_n(P, *V, O), *R = __atomic_exchange_n(P, *V, O) and __atomic_compare_exchange_n(P, E, *D, W, S, F), on the same integers and pointers.

examples 21/21 hold
✓
bind_ordinaryp, varnone, ptrlong, E0, E
atomic_builtin'__atomic_load', idp, idr, chr2, E, X
⟹X = exprcasttypetspecvoid, base, assign=, underef, idr, callid'__atomic_load_n', idp, chr2
✓
bind_ordinaryp, varnone, ptrstruct0, E0, E
atomic_builtin'__atomic_load', idp, idr, chr2, E, _
⟹fails
✓
bind_ordinaryp, varnone, ptrint, E0, E
atomic_builtin'__atomic_load_n', idp, chr0, E, X
⟹X = exprcommachr0, underef, idp
✓
bind_ordinaryp, varnone, ptrptrint, E0, E
atomic_builtin'__atomic_load_n', idp, chr2, E, X
⟹X = builtinptrint
✓
bind_ordinaryp, varnone, ptrint, E0, E
atomic_builtin'__atomic_store_n', idp, chr1, chr5, E, X
⟹X = builtinvoid
✓
bind_ordinaryp, varnone, ptrqualifiedatomic, int, E0, E
atomic_builtin'__atomic_store_n', idp, chr1, chr0, E, X
⟹X = builtinvoid
✓
bind_ordinaryp, varnone, ptrint, E0, E
atomic_builtin'__atomic_store_n', idp, chr1, chr2, E, _
⟹fails
✓
bind_ordinaryp, varnone, ptrdouble, E0, E
atomic_builtin'__atomic_load_n', idp, chr0, E, _
⟹fails
✓
bind_ordinaryp, varnone, ptrqualifiedatomic, int, E0, E
atomic_builtin'__c11_atomic_fetch_add', idp, chr1, chr5, E, X
⟹X = builtinint
✓
bind_ordinaryp, varnone, ptrqualifiedatomic, ptrchar, E0, E
bind_ordinarye, varnone, ptrptrchar, E, E1
atomic_builtin'__c11_atomic_compare_exchange_weak', idp, ide, ide, chr5, chr0, E1, X
⟹X = builtinbool
✓
bind_ordinaryp, varnone, ptrlong, E0, E
atomic_builtin'__sync_add_and_fetch', idp, chr1, E, X
⟹X = builtinlong
✓
bind_ordinaryp, varnone, ptrptrint, E0, E
atomic_builtin'__atomic_fetch_add', idp, chr1, chr5, E, _
⟹fails
✓
bind_ordinaryp, varnone, ptrqualifiedatomic, double, E0, E
atomic_builtin'__c11_atomic_fetch_add', idp, chr1, chr5, E, _
⟹fails
✓
bind_ordinaryp, varnone, ptrqualifiedatomic, double, E0, E
bind_ordinarye, varnone, ptrdouble, E, E1
atomic_builtin'__c11_atomic_compare_exchange_strong', idp, ide, chr0, chr5, chr5, E1, X
⟹X = builtinbool
✓
bind_ordinaryp, varnone, ptrdouble, E0, E
atomic_builtin'__atomic_exchange_n', idp, chr0, chr5, E, _
⟹fails
✓
bind_ordinaryp, varnone, ptrqualifiedatomic, ptrvoid, E0, E
atomic_builtin'__c11_atomic_load', idp, chr0, E, X
⟹X = builtinptrvoid
✓
bind_ordinaryp, varnone, ptrqualifiedatomic, double, E0, E
atomic_builtin'__c11_atomic_load', idp, chr5, E, X
⟹X = exprcommachr5, underef, idp
✓
bind_ordinaryp, varnone, ptrqualifiedatomic, int, E0, E
atomic_builtin'__c11_atomic_init', idp, chr1, E, X
⟹X = builtinvoid
✓
atomic_builtin'__c11_atomic_thread_fence', chr2, E, X
⟹X = builtinvoid
✓
bind_ordinaryp, varnone, ptrint, E0, E
atomic_builtin'__c11_atomic_load', idp, chr0, E, _
⟹fails
✓
bind_ordinaryp, varnone, ptrint, E0, E
atomic_builtin'__c11_atomic_fetch_add', idp, chr1, chr5, E, _
⟹fails
4144atomic_builtinName, Args, Env, exprE ←
generic_atomicName, Args, E
Args = P|_
lowered_typeP, Env, ptrT0
!

An _Atomic struct or union (atomic_su_type/3): load, store and init are its lvalue conversion and assignment, and exchange and compare-exchange builtins of its type; the arithmetic ones are refused.

4151atomic_builtin'__c11_atomic_load', P, O, Env, exprcommaO, underef, P ←
order_inO, Env, 0, 1, 2, 5
!
4153atomic_builtin'__c11_atomic_store', P, V, O, Env, exprcasttypetspecvoid, base, commaO, assign=, underef, P, V ←
order_inO, Env, 0, 3, 5
!
4156atomic_builtin'__c11_atomic_init', P, V, Env, exprcasttypetspecvoid, base, assign=, underef, P, V ←
4159atomic_builtin'__c11_atomic_exchange', P, _, O, Env, builtinS ←
4161atomic_builtinName, P, E, _, Os, Of, Env, builtinbool ←
memberchkName, '__c11_atomic_compare_exchange_strong', '__c11_atomic_compare_exchange_weak'
lowered_typeP, Env, ptrT0
unqualifiedT0, atomic_, Size
lowered_typeE, Env, ptrU0
U ≡ S
!
4169atomic_builtin'__atomic_load_n', P, O, Env, R ←
atomic_operandsP, O, Env, 0, 1, 2, 5, T
T = ptr_
→
R = builtinT
else
R = exprcommaO, underef, P
4174atomic_builtin'__atomic_store_n', P, V, O, Env, R ←
atomic_operandsP, O, Env, 0, 3, 5, T, Order
T = ptr_
or
Order =:= 5
or
→
R = builtinvoid
else
R = exprcasttypetspecvoid, base, commaO, assign=, underef, P, V
4179atomic_builtin'__c11_atomic_load', P, O, Env, R ←
c11_operandsP, O, Env, 0, 1, 2, 5, T
T = ptr_
→
R = builtinT
else
R = exprcommaO, underef, P
4185atomic_builtin'__c11_atomic_store', P, _, O, Env, builtinvoid ←
c11_operandsP, O, Env, 0, 3, 5, _
4188atomic_builtin'__c11_atomic_init', P, _, Env, builtinvoid ←
4191atomic_builtinName, O, Env, builtinvoid ←
catchconst_valueO, Env, V, _, not_constant, fail
between0, 5, V
4196atomic_builtinName, P, _, O, Env, builtinT ←
rmw_builtinName, c11, Op, _
lowered_typeP, Env, ptrT0
4201atomic_builtinName, P, E, _, Os, Of, Env, builtinbool ←
memberchkName, '__c11_atomic_compare_exchange_strong', '__c11_atomic_compare_exchange_weak'
cas_operandsP, E, Env
4207atomic_builtinName, P, _, O, Env, builtinT ←
rmw_builtinName, gnu, Op, _
lowered_typeP, Env, ptrT0
4211atomic_builtin'__atomic_compare_exchange_n', P, E, _, W, Os, Of, Env, builtinbool ←
lowered_typeP, Env, ptrT0
cas_operandsP, E, Env
catchconst_valueW, Env, _, _, not_constant, fail
4216atomic_builtinName, P, _|_, Env, builtinT ←
rmw_builtinName, sync, Op, _
lowered_typeP, Env, ptrT0
4219atomic_builtinName, P, _, _|_, Env, builtinR ←
memberchkName−R0, '__sync_val_compare_and_swap'−val, '__sync_bool_compare_and_swap'−bool
lowered_typeP, Env, ptrT0
R0 ≡ val
→
R = T
else
R = bool
4224atomic_builtin'__sync_lock_release', P|_, Env, builtinvoid ←
lowered_typeP, Env, ptrT0
4226atomic_builtin'__sync_synchronize', [], _, builtinvoid
4227atomic_builtinName, P, O, Env, builtinR ←
memberchkName−R, '__atomic_test_and_set'−bool, '__atomic_clear'−void
lowered_typeP, Env, ptrT
T ≠ func_, _, _
generic_atomic+Name, +Args, -E

GCC's generic atomic Name on Args is E, its _n form on the values the pointers in Args point to.

4234generic_atomic'__atomic_load', P, R, O, casttypetspecvoid, base, assign=, underef, R, callid'__atomic_load_n', P, O
4237generic_atomic'__atomic_store', P, V, O, callid'__atomic_store_n', P, underef, V, O
4239generic_atomic'__atomic_exchange', P, V, R, O, casttypetspecvoid, base, assign=, underef, R, callid'__atomic_exchange_n', P, underef, V, O
4243generic_atomic'__atomic_compare_exchange', P, E, D, W, S, F, callid'__atomic_compare_exchange_n', P, E, underef, D, W, S, F
rmw_builtin?Name, ?Family, ?Op, ?Resultnondet
Namein/outan atomic read-modify-write builtin
Familyin/outc11 on an _Atomic object, gnu or sync on a plain one
Opin/outhow it combines the old value and its operand (xchg: the operand alone)
Resultin/outold or new: the value it returns
GCC manual, __atomic Builtins
examples 2/2 hold
✓rmw_builtin'__atomic_add_fetch', F, Op, R⟹F, Op, R = gnu, add, new
✓rmw_builtin'__sync_fetch_and_or', F, Op, R⟹F, Op, R = sync, or, old
4255rmw_builtin'__c11_atomic_exchange', c11, xchg, old
4256rmw_builtinName, c11, Op, old ←
rmw_opOp, _
Op ≢ xchg
atom_concat'__c11_atomic_fetch_', Op, Name
4258rmw_builtin'__atomic_exchange_n', gnu, xchg, old
4259rmw_builtinName, gnu, Op, old ←
rmw_opOp, _
Op ≢ xchg
atom_concat'__atomic_fetch_', Op, Name
4261rmw_builtinName, gnu, Op, new ←
rmw_opOp, _
Op ≢ xchg
atomic_list_concat'__atomic_', Op, '_fetch', Name
4263rmw_builtin'__sync_lock_test_and_set', sync, xchg, old
4264rmw_builtinName, sync, Op, old ←
rmw_opOp, sync
atom_concat'__sync_fetch_and_', Op, Name
4266rmw_builtinName, sync, Op, new ←
rmw_opOp, sync
atomic_list_concat'__sync_', Op, '_and_fetch', Name
rmw_op?Op, ?Sync

an Op of the fetch-and-op builtins; Sync is sync for those __sync has too.

rmw_op
4271xchgnone
4272addsync
4272subsync
4272andsync
4273orsync
4273xorsync
4273nandsync
4274maxnone
4274minnone
rmw_operand+Op, +T

an _Atomic T may be combined by Op: any integer by any, a pointer by exchange, add or sub, a float or double by exchange.

4279rmw_operand_, T ←
4280rmw_operandOp, ptr_ ←
memberchkOp, xchg, add, sub
4281rmw_operandxchg, T ←
memberchkT, float, double
gnu_operand+Op, +T

GCC's builtins take an integer, or a pointer only to exchange it.

4285gnu_operand_, T ←
4286gnu_operandxchg, ptr_
cas_operands+P, +E, +Envsemidet
Pina compare-exchange's object pointer, to an integer, pointer, float or double
Einits expected-value pointer
Envinthe scope

E points to an object of *P's unqualified type, to any pointer when *P is a pointer, or is a void *: clang initializes the parameter from E as by assignment, and void * converts to any object pointer (GLib's g_atomic_int_compare_and_exchange passes void *&old).

C11 6.5.16.1p1, 6.3.2.3p1Clang, SemaChecking.cpp BuildAtomicExpr: pointer arguments are copy-initialized
4299cas_operandsP, E, Env ←
lowered_typeP, Env, ptrT0
or
T = ptr_
or
memberchkT, float, double
lowered_typeE, Env, ptrU0
U ≡ T
→
true
U ≡ void
→
true
else
T = ptr_
U = ptr_
variable_orders+Name, +Args, +Env, -Orders, -Args1semidet
Nameinan __atomic or __c11_atomic builtin
Argsinits arguments
Envinthe scope
Ordersoutits memory-order arguments that are not constant, at least one
Args1outArgs with each of those replaced by 5, __ATOMIC_SEQ_CST

clang accepts a memory order known only at run time and chooses the order then; nanofil uses seq_cst, which is at least as strong as any order, after evaluating the argument for its effects. liburcu passes cmm_to_c11(CMM_RELAXED), a call to a static inline function.

C11 7.17.3p1GCC manual, __atomic Builtins: any order may be implemented as the most restrictive, __ATOMIC_SEQ_CST
4318variable_ordersName, Args, Env, Orders, Args1 ←
order_positionsName, Args, Ps
findallI−O,
memberI, Ps
nth0I, Args, O
¬catchconst_valueO, Env, _, _, not_constant, fail
catchexpression_typeO, Env, T, nanofil_error_, fail
,
Pairs
Pairs ≢ []
pairs_valuesPairs, Orders
findallA,
nth0I, Args, A0
memberchkI−_, Pairs
→
A = num5, none−dec
else
A = A0
,
Args1
evaluated_first+O, +E0, -E

E is O, for its effects, then E0.

4332evaluated_firstO, E0, commacasttypetspecvoid, base, O, E0
order_positions+Name, +Args, -Positions

the argument positions of builtin Name's memory orders.

4336order_positionsName, Args, Ps ←
sub_atomName, 0, _, _, '__atomic_'
→
true
else
sub_atomName, 0, _, _, '__c11_atomic_'
¬memberchkName, '__c11_atomic_init', '__atomic_always_lock_free', '__atomic_is_lock_free', '__c11_atomic_is_lock_free'
lengthArgs, N
N ≥ 1
Last is N−1
sub_atomName, _, _, _, compare_exchange
→
N ≥ 2
Prev is N−2
Ps = Prev, Last
else
Ps = Last
4347order_constantO, Env ←
catchconst_valueO, Env, V, _, not_constant, fail
between0, 5, V
fence_builtin'__c11_atomic_thread_fence''__c11_atomic_signal_fence''__atomic_thread_fence''__atomic_signal_fence'
atomic_pointee+P, +Env

P points to an _Atomic object.

4357atomic_pointeeP, Env ←
atomic_su_pointee+P, +Env, -S

P points to an _Atomic S, a struct or union (atomic_su_type/3).

4362atomic_su_pointeeP, Env, S ←
lowered_typeP, Env, ptrT0
unqualifiedT0, atomicS, _
4365order_inO, Env, Orders ←
catchconst_valueO, Env, V, _, not_constant, fail
memberchkV, Orders
c11_operands+P, +O, +Env, +Orders, -T

as atomic_operands/5, with a float or double *P too.

4371c11_operandsP, O, Env, Orders, T ←
lowered_typeP, Env, ptrT0
catchconst_valueO, Env, V, _, not_constant, fail
memberchkV, Orders
4377atomic_name'__atomic_load_n'
4378atomic_name'__atomic_store_n'
4379atomic_name'__c11_atomic_load'
4380atomic_name'__c11_atomic_store'
4381atomic_name'__c11_atomic_init'
4382atomic_nameName ←
4383atomic_nameName ←
rmw_atomic?Name

an atomic read-modify-write builtin.

4386rmw_atomicName ←
rmw_builtinName, _, _, _
4387rmw_atomicName ←
memberName, '__c11_atomic_compare_exchange_strong', '__c11_atomic_compare_exchange_weak', '__atomic_compare_exchange_n', '__sync_val_compare_and_swap', '__sync_bool_compare_and_swap', '__sync_lock_release', '__sync_synchronize', '__atomic_test_and_set', '__atomic_clear'

atomic_operands(+P, +O, +Env, +Orders, -T[, -Order]): P points to an integer or a pointer of unqualified type T and O is a constant memory order Order among Orders (__ATOMIC_RELAXED 0, CONSUME 1, ACQUIRE 2, RELEASE 3, ACQ_REL 4, SEQ_CST 5).

4398atomic_operandsP, O, Env, Orders, T ←
atomic_operandsP, O, Env, Orders, T, _
4399atomic_operandsP, O, Env, Orders, T, V ←
lowered_typeP, Env, ptrT0
→
true
else
T = ptr_
catchconst_valueO, Env, V, _, not_constant, fail
memberchkV, Orders
rotate_builtin?Name, ?Dir, ?Typenondet
Namein/outa clang rotate builtin
Dirin/outleft or right
Typein/outthe unsigned type it rotates: uchar, ushort, uint or ulong
examples 1/1 hold
✓rotate_builtin'__builtin_rotateleft32', D, T⟹D, T = left, uint
4411rotate_builtinName, Dir, Type ←
memberDir−Prefix, left−'__builtin_rotateleft', right−'__builtin_rotateright'
memberBits−Type, 8−uchar, 16−ushort, 32−uint, 64−ulong
atom_concatPrefix, Bits, Name
builtin_type?Name, ?Typenondet
Namein/outa builtin's name
Typein/outthe type of a call of it

A call of builtin Name, when Name is not declared, has Type. The bit builtins count trailing or leading zero bits, or one bits, of an unsigned int, long or long long, giving an int; the byte swaps give the unsigned type of their width.

clang's rotates __builtin_rotateleftN and __builtin_rotaterightN (N 8, 16, 32 or 64) give the unsigned type of width N, as their operands are.

__builtin_isnan(x) is nonzero for a NaN of any floating type (an integer converts to double first). __builtin_flt_rounds() is FLT_ROUNDS, the current rounding direction: 0 toward zero, 1 to nearest, 2 upward, 3 downward.

__builtin_assume_aligned(p, align[, offset]) is p as a void *; the alignment it promises changes no bounds, so nanofil drops it.

GCC manual, Other Built-in FunctionsClang Language Extensions, Builtin rotation functionsC11 5.2.4.2.2p8C11 7.12.3.4p2
examples 10/10 hold
✓builtin_type'__builtin_ctzll', T⟹T = int
✓builtin_type'__builtin_bswap16', T⟹T = ushort
✓builtin_type'__builtin_bswap64', T⟹T = ulong
✓builtin_type'__builtin_rotateleft8', T⟹T = uchar
✓builtin_type'__builtin_rotateright64', T⟹T = ulong
✓builtin_type'__builtin_rotateleft128', _⟹fails
✓builtin_type'__builtin_isnan', T⟹T = int
✓builtin_type'__builtin_fabsl', T⟹T = ldouble
✓builtin_type'__builtin_assume_aligned', T⟹T = ptrvoid
✓builtin_type'__builtin_ctzlll', _⟹fails
4447builtin_type'__builtin_va_start', void
4448builtin_type'__builtin_va_copy', void
4449builtin_type'__builtin_va_end', void
4450builtin_type'__builtin_unreachable', void
4451builtin_type'__builtin_alloca', ptrvoid
4452builtin_type'__builtin_frame_address', ptrvoid
4453builtin_type'__builtin_expect', long
4454builtin_type'__builtin_assume_aligned', ptrvoid
4455builtin_type'__builtin_constant_p', int
4456builtin_type'__builtin_cpu_init', void
4457builtin_type'__builtin_cpu_supports', int
4458builtin_type'__builtin_cpu_is', int
4459builtin_typeName, bool ←
4460builtin_typeName, int ←
bit_builtinName, _, _
4461builtin_type'__builtin_bswap16', ushort
4462builtin_type'__builtin_bswap32', uint
4463builtin_type'__builtin_bswap64', ulong
4464builtin_typeName, Type ←
rotate_builtinName, _, Type
4465builtin_typeName, double ←
4466builtin_typeName, T ←
sign_builtinName, _, T
4467builtin_typeName, T ←
root_builtinName, _, T
4468builtin_typeName, T ←
4469builtin_type'__builtin_trap', void
4470builtin_type'__builtin_ia32_rdtsc', ullong
4471builtin_type'__builtin_isnan', int
4472builtin_type'__builtin_flt_rounds', int
4473builtin_type'__builtin_object_size', ulong
4474builtin_type'__builtin_dynamic_object_size', ulong
4475builtin_typeName, int ←
4476builtin_typeName, T ←
vector_builtinName, _, T
cpu_feature?Name, ?Sym, ?Off, ?Masknondet
Namein/outa feature __builtin_cpu_supports(Name) tests
Symin/outthe runtime's word array, __cpu_model or __cpu_features2
Offin/outthe byte offset of the 32-bit word in it
Maskin/outthe feature's bits in that word

__builtin_cpu_supports("name") is 1 when the word at Off in Sym, which __cpu_indicator_init (__builtin_cpu_init) fills from cpuid, has all of Mask set, else 0, as clang's x86 lowering reads it; any other string is 0 (clang warns: an invalid cpu feature string). The table is measured from the pinned filcc's IR for each name it accepts.

GCC manual, x86 Built-in Functions, __builtin_cpu_supportscompiler-rt lib/builtins/cpu_model/x86.c, struct __processor_model and __cpu_features2
examples 3/3 hold
✓cpu_featureavx2, '__cpu_model', 12, 1024
✓cpu_feature'x86-64-v3', '__cpu_features2', 8, 2
✓cpu_featurelm, _, _, _⟹fails
cpu_feature
4494cmov'__cpu_model'120x1
4495mmx'__cpu_model'120x2
4496popcnt'__cpu_model'120x4
4497sse'__cpu_model'120x8
4498sse2'__cpu_model'120x10
4499sse3'__cpu_model'120x20
4500ssse3'__cpu_model'120x40
4501'sse4.1''__cpu_model'120x80
4502'sse4.2''__cpu_model'120x100
4503avx'__cpu_model'120x200
4504avx2'__cpu_model'120x400
4505sse4a'__cpu_model'120x800
4506fma4'__cpu_model'120x1000
4507xop'__cpu_model'120x2000
4508fma'__cpu_model'120x4000
4509avx512f'__cpu_model'120x8000
4510bmi'__cpu_model'120x10000
4511bmi2'__cpu_model'120x20000
4512aes'__cpu_model'120x40000
4513pclmul'__cpu_model'120x80000
4514avx512vl'__cpu_model'120x100000
4515avx512bw'__cpu_model'120x200000
4516avx512dq'__cpu_model'120x400000
4517avx512cd'__cpu_model'120x800000
4518avx512vbmi'__cpu_model'120x4000000
4519avx512ifma'__cpu_model'120x8000000
4520avx5124vnniw'__cpu_model'120x10000000
4521avx5124fmaps'__cpu_model'120x20000000
4522avx512vpopcntdq'__cpu_model'120x40000000
4523avx512vbmi2'__cpu_model'120x80000000
4524gfni'__cpu_features2'00x1
4525vpclmulqdq'__cpu_features2'00x2
4526avx512vnni'__cpu_features2'00x4
4527avx512bitalg'__cpu_features2'00x8
4528avx512bf16'__cpu_features2'00x10
4529avx512vp2intersect'__cpu_features2'00x20
4530adx'__cpu_features2'00x100
4531cldemote'__cpu_features2'00x400
4532clflushopt'__cpu_features2'00x800
4533clwb'__cpu_features2'00x1000
4534clzero'__cpu_features2'00x2000
4535enqcmd'__cpu_features2'00x10000
4536f16c'__cpu_features2'00x20000
4537fsgsbase'__cpu_features2'00x40000
4538lwp'__cpu_features2'00x1000000
4539lzcnt'__cpu_features2'00x2000000
4540movbe'__cpu_features2'00x4000000
4541movdir64b'__cpu_features2'00x8000000
4542movdiri'__cpu_features2'00x10000000
4543mwaitx'__cpu_features2'00x20000000
4544pconfig'__cpu_features2'00x80000000
4545pku'__cpu_features2'40x1
4546prfchw'__cpu_features2'40x4
4547ptwrite'__cpu_features2'40x8
4548rdpid'__cpu_features2'40x10
4549rdrnd'__cpu_features2'40x20
4550rdseed'__cpu_features2'40x40
4551rtm'__cpu_features2'40x80
4552serialize'__cpu_features2'40x100
4553sgx'__cpu_features2'40x200
4554sha'__cpu_features2'40x400
4555shstk'__cpu_features2'40x800
4556tbm'__cpu_features2'40x1000
4557tsxldtrk'__cpu_features2'40x2000
4558vaes'__cpu_features2'40x4000
4559waitpkg'__cpu_features2'40x8000
4560wbnoinvd'__cpu_features2'40x10000
4561xsave'__cpu_features2'40x20000
4562xsavec'__cpu_features2'40x40000
4563xsaveopt'__cpu_features2'40x80000
4564xsaves'__cpu_features2'40x100000
4565'amx-tile''__cpu_features2'40x200000
4566'amx-int8''__cpu_features2'40x400000
4567'amx-bf16''__cpu_features2'40x800000
4568uintr'__cpu_features2'40x1000000
4569hreset'__cpu_features2'40x2000000
4570kl'__cpu_features2'40x4000000
4571widekl'__cpu_features2'40x10000000
4572avxvnni'__cpu_features2'40x20000000
4573avx512fp16'__cpu_features2'40x40000000
4574'x86-64''__cpu_features2'40x80000000
4575'x86-64-v2''__cpu_features2'80x1
4576'x86-64-v3''__cpu_features2'80x2
4577'x86-64-v4''__cpu_features2'80x4
4578avxifma'__cpu_features2'80x8
4579avxvnniint8'__cpu_features2'80x10
4580avxneconvert'__cpu_features2'80x20
4581cmpccxadd'__cpu_features2'80x40
4582'amx-fp16''__cpu_features2'80x80
4583prefetchi'__cpu_features2'80x100
4584raoint'__cpu_features2'80x200
4585'amx-complex''__cpu_features2'80x400
4586avxvnniint16'__cpu_features2'80x800
4587sm3'__cpu_features2'80x1000
4588sha512'__cpu_features2'80x2000
4589sm4'__cpu_features2'80x4000
4590apxf'__cpu_features2'80x8000
4591usermsr'__cpu_features2'80x10000
4592'avx10.1-256''__cpu_features2'80x20000
4593'avx10.1-512''__cpu_features2'80x40000
4594'avx10.2-256''__cpu_features2'80x80000
4595'avx10.2-512''__cpu_features2'80x100000
cpu_model_value?Name, ?Off, ?Valuenondet
Namein/outa vendor, processor type or subtype __builtin_cpu_is(Name) tests
Offin/outthe byte offset in __cpu_model of the vendor (0), type (4) or subtype (8)
Valuein/outthe value that word has for Name

__builtin_cpu_is("name") is 1 when the 32-bit word at Off in __cpu_model equals Value, else 0; clang refuses any other name. Measured from the pinned filcc's IR.

GCC manual, x86 Built-in Functions, __builtin_cpu_is
examples 3/3 hold
✓cpu_model_valueintel, 0, 1
✓cpu_model_valuehaswell, 8, 13
✓cpu_model_valuek8, _, _⟹fails
cpu_model_value
4609intel01
4610amd02
4611atom41
4612core242
4613corei743
4614amdfam10h44
4615amdfam15h45
4616amdfam17h410
4617amdfam19h415
4618amdfam1ah420
4619bonnell41
4620silvermont46
4621slm46
4622knl47
4623knm411
4624goldmont412
4625'goldmont-plus'413
4626tremont414
4627sierraforest417
4628grandridge418
4629clearwaterforest419
4630nehalem81
4631westmere82
4632sandybridge83
4633ivybridge812
4634haswell813
4635broadwell814
4636skylake815
4637'skylake-avx512'816
4638cannonlake817
4639'icelake-client'818
4640'icelake-server'819
4641cascadelake821
4642tigerlake822
4643cooperlake823
4644sapphirerapids824
4645alderlake825
4646raptorlake825
4647meteorlake825
4648rocketlake827
4649graniterapids830
4650'graniterapids-d'831
4651arrowlake832
4652'arrowlake-s'833
4653lunarlake833
4654pantherlake834
4655gracemont825
4656emeraldrapids824
4657diamondrapids836
4658barcelona84
4659shanghai85
4660istanbul86
4661btver148
4662btver249
4663bdver187
4664bdver288
4665bdver389
4666bdver4810
4667znver1811
4668znver2820
4669znver3826
4670znver4829
4671znver5835
classify_builtin?Namenondet
Namein/outa type-generic classification builtin glibc's math.h uses

__builtin_isinf_sign(x) is 1 for +inf, -1 for -inf, else 0; __builtin_isfinite, __builtin_isnormal and __builtin_signbit are 1 or 0; __builtin_fpclassify(Nan, Inf, Normal, Subnormal, Zero, x) is the argument for x's class. A float classifies as itself, an integer or double as a double, and a long double by its x87 bits, as clang does: an unnormal, pseudo-infinity or pseudo-NaN (a nonzero exponent without the explicit integer bit) is a NaN, and a pseudo-denormal (a zero exponent with it) is FP_NORMAL to fpclassify but not isnormal. __builtin_signbitl(x) is __builtin_signbit((long double)x), and __builtin_signbitf(x) __builtin_signbit((float)x).

__builtin_isinf(x) is 1 for either infinity, else 0.

Intel 64 and IA-32 Architectures Software Developer's Manual, Volume 1, 8.2.2 Unsupported Double Extended-Precision Floating-Point Encodings and Pseudo-DenormalsC11 7.12.3GCC manual, Other Built-in Functions
examples 2/2 hold
✓classify_builtin'__builtin_fpclassify'
✓classify_builtin'__builtin_isinf'
classify_builtin'__builtin_isinf''__builtin_isinf_sign''__builtin_isfinite''__builtin_isnormal''__builtin_signbit''__builtin_signbitl''__builtin_signbitf''__builtin_fpclassify'
libm_builtin?Name, ?Fnondet
Namein/outa GNU math builtin
Fin/outthe libm function it computes

__builtin_ceil(x) and __builtin_floor(x) are ceil(x) and floor(x) of x converted to double, even with F undeclared. Clang computes them inline (roundsd); a call of libm's F gives the same exact result, signed zeros and NaNs included.

GCC manual, Other Built-in FunctionsC11 7.12.9.1p2C11 7.12.9.2p2
examples 3/3 hold
✓libm_builtin'__builtin_floor', F⟹F = floor
✓libm_builtin'__builtin_sqrt', _⟹fails
✓libm_builtin'__builtin_round', _⟹fails
4715libm_builtin'__builtin_ceil', ceil
4716libm_builtin'__builtin_floor', floor
root_builtin?Name, ?F, ?Typenondet
Namein/outa GNU square root builtin
Fin/outthe libm function it computes
Typein/outthe type of its argument and result

__builtin_sqrt(x), __builtin_sqrtf(x) and __builtin_sqrtl(x) are the square root of x converted to Type, correctly rounded: the IR's sqrt under -fno-math-errno (NANOFIL_MATH_ERRNO=0), as glibc's libm is built and where its own sqrt is this builtin, so a call would recurse. Under the default -fmath-errno libm's F also sets errno to EDOM for x < 0, so the builtin is a call of F, as clang's is (cogl's cogl_sqrti); a long double one is then refused.

C11 7.12.7.5GCC manual, Other Built-in Functions
examples 3/3 hold
✓root_builtin'__builtin_sqrt', F, T⟹F, T = sqrt, double
✓root_builtin'__builtin_sqrtl', F, T⟹F, T = sqrtl, ldouble
✓root_builtin'__builtin_sqrtll', _, _⟹fails
root_builtin
4735'__builtin_sqrt'sqrtdouble
4736'__builtin_sqrtf'sqrtffloat
4737'__builtin_sqrtl'sqrtlldouble
trunc_builtin?Name, ?Typenondet
Namein/outa GNU builtin that rounds toward zero
Typein/outthe type of its argument and result, float or double

__builtin_trunc(x) and __builtin_truncf(x) are x rounded toward zero: a magnitude below 2^52 (2^23 for float) through the integer it truncates to, with x's sign so -0.5 gives -0; any other x is already integral, infinite or a NaN, which x + 0 quiets. glibc's own trunc and truncf are these builtins. trunc never sets errno.

C11 7.12.9.8p2GCC manual, Other Built-in Functions
examples 2/2 hold
✓trunc_builtin'__builtin_truncf', T⟹T = float
✓trunc_builtin'__builtin_truncl', _⟹fails
4752trunc_builtin'__builtin_trunc', double
4753trunc_builtin'__builtin_truncf', float
sign_builtin?Name, ?Op, ?Typenondet
Namein/outa GNU builtin that edits a float's sign bit
Opin/outabs, which clears it, or copysign, which takes it from the second argument
Typein/outthe type of its arguments and result

__builtin_fabs(x) is x with its sign bit clear, and __builtin_copysign(x, y) is x with y's sign bit, with their f (float) and l (long double) forms: only that bit changes, so a NaN keeps its payload and -0 becomes +0. clang computes them inline (llvm.fabs, llvm.copysign), and glibc's own fabs and copysign are these builtins, so they are never a call.

C11 7.12.7.2p2C11 7.12.11.1p2GCC manual, Other Built-in Functions
examples 3/3 hold
✓sign_builtin'__builtin_fabsf', Op, T⟹Op, T = abs, float
✓sign_builtin'__builtin_copysignl', Op, T⟹Op, T = copysign, ldouble
✓sign_builtin'__builtin_fabsll', _, _⟹fails
sign_builtin
4772'__builtin_fabs'absdouble
4773'__builtin_fabsf'absfloat
4774'__builtin_fabsl'absldouble
4775'__builtin_copysign'copysigndouble
4776'__builtin_copysignf'copysignfloat
4777'__builtin_copysignl'copysignldouble
vector_builtin?Name, ?Params, ?Retnondet
Namein/outa clang builtin that immintrin.h's SSE through SSE4.2 intrinsics call
Paramsin/outits parameter types
Retin/outits result type

c_lower computes each element by element (vector_builtin//4). A count, lane or shuffle-control parameter typed int must be a constant where the instruction encodes it as an immediate.

Clang Builtins, X86 (clang/include/clang/Basic/BuiltinsX86.td)Intel 64 and IA-32 Architectures Software Developer's Manual, Volume 2
examples 2/2 hold
✓vector_builtin'__builtin_ia32_pmovmskb128', vecchar, 16, 16, int
✓vector_builtin'__builtin_ia32_psrlwi128', P, R⟹P = vecshort, 8, 16, int, R = vecshort, 8, 16
4792vector_builtinName, Params, Ret ←
vector_builtin_Short, Params0, Ret0
atom_concat'__builtin_ia32_', Short, Name
maplistvector_abbrev, Params0, Params
vector_abbrevRet0, Ret
4798vector_abbrevv16qi, vecchar, 16, 16 ←
!
4799vector_abbrevv8hi, vecshort, 8, 16 ←
!
4800vector_abbrevv4si, vecint, 4, 16 ←
!
4801vector_abbrevv2di, vecllong, 2, 16 ←
!
4802vector_abbrevv4sf, vecfloat, 4, 16 ←
!
4803vector_abbrevv2df, vecdouble, 2, 16 ←
!
4804vector_abbrevv32qi, vecchar, 32, 32 ←
!
4805vector_abbrevv16hi, vecshort, 16, 32 ←
!
4806vector_abbrevv8si, vecint, 8, 32 ←
!
4807vector_abbrevv4di, vecllong, 4, 32 ←
!
4808vector_abbrevv8sf, vecfloat, 8, 32 ←
!
4809vector_abbrevv4df, vecdouble, 4, 32 ←
!
4810vector_abbrevT, T
4812vector_builtin_pmovmskb128, v16qi, int
4813vector_builtin_Name256, Ps, R ←
AVX2: per 128-bit lane
lane_builtinName256, Name128
vector_builtin_Name128, Ps0, R0
→
Ps0 = P0, C
Ps = P, C
else
maplistwide_abbrev, Ps0, Ps
4821vector_builtin_pmovmskb256, v32qi, int
4822vector_builtin_addsubps, v4sf, v4sf, v4sf
4823vector_builtin_addsubpd, v2df, v2df, v2df
4824vector_builtin_Name, V, V, V, V ←
memberV−Ws, v4sf−vfmaddps, vfmaddsubps, v2df−vfmaddpd, vfmaddsubpd
memberName, Ws
4827vector_builtin_vpermilps, v4sf, int, v4sf
4828vector_builtin_vpermilpd, v2df, int, v2df
4829vector_builtin_vpermilpd256, v4df, int, v4df
4830vector_builtin_shufpd256, v4df, v4df, int, v4df
4831vector_builtin_movmskps256, v8sf, int
4832vector_builtin_movmskpd256, v4df, int
4833vector_builtin_Name, W, int, H ←
memberName−W−H, extract128i256−v4di−v2di, vextractf128_si256−v8si−v4si, vextractf128_ps256−v8sf−v4sf, vextractf128_pd256−v4df−v2df
4836vector_builtin_Name, W, H, int, W ←
memberName−W−H, insert128i256−v4di−v2di, vinsertf128_si256−v8si−v4si, vinsertf128_ps256−v8sf−v4sf, vinsertf128_pd256−v4df−v2df
4839vector_builtin_Name, W, W, int, W ←
memberName−W, permti256−v4di, vperm2f128_si256−v8si, vperm2f128_ps256−v8sf, vperm2f128_pd256−v4df
4842vector_builtin_permvarsi256, v8si, v8si, v8si
4843vector_builtin_permvarsf256, v8sf, v8si, v8sf
4844vector_builtin_Name, W, int, E ←
memberName−W−E, vec_ext_v32qi−v32qi−char, vec_ext_v16hi−v16hi−short, vec_ext_v8si−v8si−int, vec_ext_v4di−v4di−llong
4847vector_builtin_vzeroupper, [], void
4848vector_builtin_vzeroall, [], void
4849vector_builtin_lddqu, ptrqualifiedconst, char, v16qi
4850vector_builtin_movmskps, v4sf, int
4851vector_builtin_movmskpd, v2df, int
4852vector_builtin_Name, V, int, V ←
memberV−Ws, v8hi−psllwi128, psrlwi128, psrawi128, v4si−pslldi128, psrldi128, psradi128, v2di−psllqi128, psrlqi128, pslldqi128_byteshift, psrldqi128_byteshift
memberName, Ws
4857vector_builtin_Name, V, V, V ←
memberV−Ws, v8hi−psllw128, psrlw128, psraw128, pmulhuw128, pmulhw128, pavgw128, psignw128, phaddw128, phsubw128, phaddsw128, phsubsw128, v4si−pslld128, psrld128, psrad128, psignd128, phaddd128, phsubd128, v2di−psllq128, psrlq128, v16qi−pshufb128, pavgb128, psignb128
memberName, Ws
4864vector_builtin_packsswb128, v8hi, v8hi, v16qi
4865vector_builtin_packuswb128, v8hi, v8hi, v16qi
4866vector_builtin_packssdw128, v4si, v4si, v8hi
4867vector_builtin_packusdw128, v4si, v4si, v8hi
4868vector_builtin_pshufd, v4si, int, v4si
4869vector_builtin_pshuflw, v8hi, int, v8hi
4870vector_builtin_pshufhw, v8hi, int, v8hi
4871vector_builtin_palignr128, v16qi, v16qi, int, v16qi
4872vector_builtin_pblendvb128, v16qi, v16qi, v16qi, v16qi
4873vector_builtin_pblendw128, v8hi, v8hi, int, v8hi
4874vector_builtin_pmuludq128, v4si, v4si, v2di
4875vector_builtin_pmuldq128, v4si, v4si, v2di
4876vector_builtin_pclmulqdq128, v2di, v2di, char, v2di
4877vector_builtin_shufpd, v2df, v2df, int, v2df
4878vector_builtin_shufps, v4sf, v4sf, int, v4sf
4879vector_builtin_Name, Ps, R ←
SSE4.2 string compares
memberL−Ps, i−v16qi, v16qi, char, e−v16qi, int, v16qi, int, char
memberO−R, m−v16qi, i−int, ia−int, ic−int, io−int, (is)−int, iz−int
atomic_list_concatpcmp, L, str, O, '128', Name
4883vector_builtin_pmaddwd128, v8hi, v8hi, v4si
4884vector_builtin_pmaddubsw128, v16qi, v16qi, v8hi
4885vector_builtin_psadbw128, v16qi, v16qi, v2di
4886vector_builtin_ptestz128, v2di, v2di, int
4887vector_builtin_ptestc128, v2di, v2di, int
4888vector_builtin_ptestnzc128, v2di, v2di, int
4889vector_builtin_vec_ext_v16qi, v16qi, int, char
4890vector_builtin_vec_ext_v8hi, v8hi, int, short
4891vector_builtin_vec_ext_v4si, v4si, int, int
4892vector_builtin_vec_ext_v2di, v2di, int, llong
4893vector_builtin_vec_ext_v4sf, v4sf, int, float
4894vector_builtin_vec_set_v16qi, v16qi, char, int, v16qi
4895vector_builtin_vec_set_v8hi, v8hi, short, int, v8hi
4896vector_builtin_vec_set_v4si, v4si, int, int, v4si
4897vector_builtin_vec_set_v2di, v2di, llong, int, v2di
4898vector_builtin_undef128, [], v2df
4899vector_builtin_cvtss2si, v4sf, int
4900vector_builtin_cvtss2si64, v4sf, llong
4901vector_builtin_cvtsd2si, v2df, int
4902vector_builtin_cvtsd2si64, v2df, llong
4903vector_builtin_cvtpd2ps, v2df, v4sf
lane_builtin?Name256, ?Name128nondet
Name256in/outan AVX2 builtin on 32-byte vectors
Name128in/outthe SSE builtin it applies to each 128-bit lane

Most AVX2 integer instructions operate on the two 128-bit lanes of their 32-byte operands independently, each as the SSE instruction of the same name does, with the same immediate; a shift's count vector is 16 bytes and applies to both lanes (shift_count_builtin/1).

Intel 64 and IA-32 Architectures Software Developer's Manual, Volume 2, VEX.256 encodings
examples 3/3 hold
✓lane_builtinpshufb256, pshufb128
✓lane_builtinpsrad256, psrad128
✓lane_builtinpermvarsi256, _⟹fails
4917lane_builtinName256, Name128 ←
memberName128, packsswb128, packuswb128, packssdw128, packusdw128, pavgb128, pavgw128, pblendvb128, pblendw128, pmaddwd128, pmaddubsw128, pmuldq128, pmuludq128, pmulhw128, pmulhuw128, psadbw128, pshufb128, psignb128, psignw128, psignd128, phaddw128, phaddd128, phaddsw128, phsubw128, phsubd128, phsubsw128, psllwi128, pslldi128, psllqi128, psrlwi128, psrldi128, psrlqi128, psrawi128, psradi128, psllw128, pslld128, psllq128, psrlw128, psrld128, psrlq128, psraw128, psrad128, pslldqi128_byteshift, psrldqi128_byteshift, palignr128, pclmulqdq128, shufps, addsubps, addsubpd, vfmaddps, vfmaddpd, vfmaddsubps, vfmaddsubpd, vpermilps
atom_concatStem, '128', Name128
→
true
else
Stem = Name128
sub_atomStem, B, _, 0, '128_byteshift'
→
sub_atomStem, 0, B, _, S0
atom_concatS0, '256_byteshift', Name256
else
atom_concatStem, '256', Name256
4938lane_builtinpshufd256, pshufd
4939lane_builtinpshuflw256, pshuflw
4940lane_builtinpshufhw256, pshufhw
4942shift_count_builtinName ←
memberchkName, psllw128, pslld128, psllq128, psrlw128, psrld128, psrlq128, psraw128, psrad128
4946wide_abbrevN, W ←
memberchkN−W, v16qi−v32qi, v8hi−v16hi, v4si−v8si, v2di−v4di, v4sf−v8sf, v2df−v4df
→
true
else
W = N
generic_vector_builtin?Namenondet
Namein/outa clang builtin whose result type follows its operands

__builtin_shufflevector(A, B, I...) picks elements of A and B, a vector of as many elements as there are constant indexes I; __builtin_convertvector(V, T) converts each element of V to T's; the elementwise builtins apply to each element and have their operand's type. __builtin_nondeterministic_value(V) is some value of V's type, V unevaluated (avxintrin.h's undefined upper halves); nanofil makes it zero.

Clang Language Extensions, __builtin_shufflevector, __builtin_convertvector, Vector Builtins
4964generic_vector_builtin'__builtin_shufflevector'
4965generic_vector_builtin'__builtin_convertvector'
4966generic_vector_builtin'__builtin_nondeterministic_value'
4967generic_vector_builtinName ←
memberOp, add_sat, sub_sat, min, max, abs
atom_concat'__builtin_elementwise_', Op, Name
vector_builtin_type+Name, +Args, +Env, -Tsemidet
Nameina generic vector builtin (__builtin_shufflevector, __builtin_convertvector, __builtin_elementwise_*)
Argsinthe call's arguments
Envinthe scope
Toutthe vec(E, N, Size) type of the call

Fails when the operands' vector types do not agree as Name needs.

4978vector_builtin_type'__builtin_shufflevector', A, B|Is, Env, vecE, K, Size ←
!
operandA, Env, _, vecE, N, _
operandB, Env, _, vecE2, N2, _
E ≡ E2
N ≡ N2
lengthIs, K
type_size_alignE, Env, ES, _
Size is ES × K
4981vector_builtin_type'__builtin_convertvector', A, TN, Env, T ←
!
operandA, Env, _, vec_, N, _
type_name_typeTN, T0, Env, _
T = vec_, N, _
4984vector_builtin_type_, A|Bs, Env, T ←
operandA, Env, _, T
T = vecE, N, _
forallmemberB, Bs, operandB, Env, _, vecE, N, _
bit_builtin?Name, ?Op, ?Typenondet
Namein/outa bit-counting builtin
Opin/outctz, clz, popcount or ffs (one plus the index of the lowest set bit, 0 for 0)
Typein/outits operand's type: uint, ulong or ullong
GCC manual, Other Built-in Functions
examples 4/4 hold
✓bit_builtin'__builtin_clzl', Op, T⟹Op, T = clz, ulong
✓bit_builtin'__builtin_popcount', Op, T⟹Op, T = popcount, uint
✓bit_builtin'__builtin_ffsl', Op, T⟹Op, T = ffs, ulong
✓bit_builtin'__builtin_parity', _, _⟹fails
4998bit_builtinName, Op, Type ←
memberOp, ctz, clz, popcount, ffs
memberSuffix−Type, ''−uint, l−ulong, ll−ullong
atomic_list_concat'__builtin_', Op, Suffix, Name
bit_folded+Name, +A, +Env, -Vsemidet
Nameina bit-counting builtin (bit_builtin/3)
Ainits argument expression
Envinthe scope
Voutthe count, when A is an integer constant expression

Clang folds a bit-counting builtin of a constant, so it may size an array or initialize a static (rhash's rhash_popcount). The operand converts to the builtin's unsigned type first. clz and ctz of 0 are undefined and do not fold.

C11 6.6p10GCC manual, Other Built-in Functions
examples 5/5 hold
✓
bit_folded'__builtin_popcount', unneg, num1, none−dec, E, V
⟹V = 32
✓
bit_folded'__builtin_clzl', num1, none−dec, E, V
⟹V = 63
✓
bit_folded'__builtin_ctz', num8, none−dec, E, V
⟹V = 3
✓
bit_folded'__builtin_ffs', num0, none−dec, E, V
⟹V = 0
✓
bit_folded'__builtin_ctz', num0, none−dec, E, _
⟹fails
5019bit_foldedName, A, Env, V ←
bit_builtinName, Op, Type
catchconst_valueA, Env, V0, _, not_constant, fail
integerV0
int_infoType, Bytes, _
Bits is 8 × Bytes
X is V0 mod 1 << Bits
bit_count_valueOp, Bits, X, V
5027bit_count_valuepopcount, _, X, V ←
V is popcountX
5028bit_count_valueffs, _, X, V ←
X =:= 0
→
V = 0
else
V is lsbX + 1
5029bit_count_valuectz, _, X, V ←
X > 0
V is lsbX
5030bit_count_valueclz, Bits, X, V ←
X > 0
V is Bits−1−msbX
overflow_builtin?Name, ?Op, ?Fixednondet
Namein/outa GNU checked-arithmetic builtin
Opin/outadd, sub or mul
Fixedin/outthe type of its operands, or generic for any integer types
GCC manual, Integer Overflow Builtins
examples 2/2 hold
✓overflow_builtin'__builtin_saddl_overflow', Op, T⟹Op, T = add, long
✓overflow_builtin'__builtin_mul_overflow', Op, T⟹Op, T = mul, generic
5040overflow_builtinName, Op, Fixed ←
memberOp, add, sub, mul
atomic_list_concat'__builtin_', Op, '_overflow', Name
Fixed = generic
or
memberPrefix−Suffix−Fixed, s−''−int, u−''−uint, s−l−long, u−l−ulong, s−ll−llong, u−ll−ullong
atomic_list_concat'__builtin_', Prefix, Op, Suffix, '_overflow', Name
block_value_type+Items, +Env, -T

T is the value type of a GNU statement expression with block Items: its last item's, once the items before it have declared their names.

5053block_value_type[], _, void
5054block_value_typeItem, Env, T ←
!
last_item_typeItem, Env, T
5055block_value_typedecl_, Specs, Inits|Items, Env0, T ←
!
decl_typesSpecs, Inits, Decls, Env0, Env1
foldlbind_decl, Decls, Env1, Env
block_value_typeItems, Env, T
5059block_value_type_|Items, Env, T ←
block_value_typeItems, Env, T
5061bind_decldName, Type, _, _, _, Env0, Env ←
bind_ordinaryName, varnone, Type, Env0, Env
5064last_item_typeat_, exprE, Env, T ←
!
operandE, Env, _, T
5065last_item_typeat_, label_, Body, Env, T ←
!
last_item_typeBody, Env, T
5066last_item_type_, _, void

Lvalues, values and designators

tree_type+X, -Tdet
Xina typed tree
Toutits type
examples 1/1 hold
✓tree_typefunfuncint, [], fixed, idf, T⟹T = funcint, [], fixed
5075tree_typeX, T ←
arg1, X, T
tree_category+X, -Catdet
Xina typed tree
Catoutlvalue, function or value, as its principal functor says
examples 2/2 hold
✓tree_categoryobjint, idx, C⟹C = lvalue
✓tree_categoryvalint, int1, C⟹C = value
tree_category
5084obj_, _lvalue
5085fun_, _function
5086val_, _value
value+X, -Vdet
Xina typed tree
VoutX used for its value

An array becomes a pointer to its first element, a function a pointer to it, and another lvalue the value it holds, unqualified.

examples 4/4 hold
✓valueobjqualifiedconst, int, idc, V⟹V = valint, loadobjqualifiedconst, int, idc
✓valueobjarrayint, 4, ida, V⟹V = valptrint, decayobjarrayint, 4, ida
✓valuefunfuncint, [], fixed, idf, V⟹V = valptrfuncint, [], fixed, decayfunfuncint, [], fixed, idf
✓valuevalint, int7, V⟹V = valint, int7
5104valueobjT, bitfieldM, W, valV, loadobjT, bitfieldM, W ←
5106valueobjT, F, valV, decayobjT, F ←
5107valueobjT, F, valV, loadobjT, F ←
5108valuefunT, F, valptrT, decayfunT, F
5109valuevalT, F, valV, F ←
bitfield_promotion+T, +W, -Pdet
Tinthe unqualified integer type of a bit-field
Winits width in bits
Poutthe type of its value

A bit-field's value is an int if an int can represent all its values, otherwise an unsigned int if that can, otherwise of its own type. C11 says so for _Bool, int and unsigned int; clang and GCC treat bit-fields of the other integer types alike.

examples 7/7 hold
✓bitfield_promotionuint, 31, P⟹P = int
✓bitfield_promotionuint, 32, P⟹P = uint
✓bitfield_promotionint, 32, P⟹P = int
✓bitfield_promotionulong, 32, P⟹P = uint
✓bitfield_promotionulong, 33, P⟹P = ulong
✓bitfield_promotionlong, 33, P⟹P = long
✓bitfield_promotionbool, 1, P⟹P = int
5128bitfield_promotionT, W, P ←
int_infoT, _, Sign
Sign ≡ signed
→
W ≤ 32
else
W < 32
→
P = int
W ≤ 32
→
P = uint
else
P = T
5135array_typequalified_, T ←
5136array_typearray_, _
value_type+T, -Vdet
Tinthe type of an expression
Voutthe type of its value

value/2 on types alone.

examples 3/3 hold
✓value_typequalified(volatile), long, V⟹V = long
✓value_typearraychar, 3, V⟹V = ptrchar
✓value_typefuncvoid, unspecified, fixed, V⟹V = ptrfuncvoid, unspecified, fixed
5147value_typequalified_, T, R ←
5148value_typeatomicS, _, S ←
!
5149value_typearrayT, _, ptrT ←
!
5150value_typefuncR, P, V, ptrfuncR, P, V ←
!
5151value_typeT, T
operand+E, +Env, -XV, -T

XV is E elaborated and used for its value, of type T.

5155operandE, Env, XV, T ←
elaborateE, Env, X
valueX, XV
expression_type+E, +Env, -Tsemidet
Eina C expression
Envinthe scope it is read in
Toutits reflected type, unevaluated

Arrays and functions keep their types until a caller asks for value conversion.

examples 1/1 hold
✓
bind_ordinarya, varnone, arrayint, 4, E0, E
⟹T = arrayint, 4
5167expression_typeE, Env, T ←
elaborateE, Env, X
expression_category+E, +Env, -Catsemidet
Eina C expression
Envinthe scope it is read in
Catoutlvalue, function or value
examples 3/3 hold
✓
bind_ordinaryx, varnone, int, E0, E
⟹C = lvalue
✓
bind_ordinaryf, fnfuncint, [], fixed, f, extern, E0, E
⟹C = function
✓
bind_ordinaryx, varnone, int, E0, E
expression_categorybinadd, idx, chr0, E, C
⟹C = value
5184expression_categoryE, Env, Cat ←
elaborateE, Env, X
Cat = Cat0
modifiable_lvalue+E, +Envsemidet
Eina C expression
Envinthe scope it is read in

E is an lvalue whose type is not const, an array or incomplete, nor a struct or union with a const member, however deeply contained.

examples 4/4 hold
✓
bind_ordinaryx, varnone, int, E0, E
✓
bind_ordinaryc, varnone, int, E0, E1
bind_ordinaryqtypec, qualifiedconst, int, E1, E
⟹fails
✓
bind_ordinarya, varnone, arrayint, 4, E0, E
⟹fails
✓⟹fails
5202modifiable_lvalueE, Env ←
elaborateE, Env, X
modifiable+X, +Env

typed tree X designates a modifiable object.

5205modifiableobjT, _, Env ←
5207modifiable_typequalifiedQs, T, Env ←
!
¬memberchkconst, Qs
5209modifiable_typearray_, _, _ ←
!
fail
5210modifiable_typevoid, _ ←
!
fail
5211modifiable_typeT, Env ←
¬const_withinT, Env
const_within+T, +Env

T is const, or an aggregate or union with a const member or element, at any depth.

5215const_withinqualifiedQs, _, _ ←
memberchkconst, Qs
!
5216const_withinqualified_, T, Env ←
5217const_withinarrayT, _, Env ←
5218const_withinSU, Env ←
SU = structId
or
SU = unionId
Env = e_, _, Defs, _
get_assocqmembersId, Defs, Members
memberq_, T, Members
!

Operators

unary_operand+Op, +T0, -Tsemidet
Opinplus, neg, bnot or not
T0inthe value type of the operand
Toutthe type of the result

+ and - need an arithmetic operand and ~ an integer, which they promote; ! needs a scalar and gives an int.

examples 5/5 hold
✓unary_operandneg, char, T⟹T = int
✓unary_operandplus, ullong, T⟹T = ullong
✓unary_operandnot, ptrint, T⟹T = int
✓unary_operandbnot, double, _⟹fails
✓unary_operandneg, ptrint, _⟹fails
5239unary_operandOp, T0, T0 ←
GNU vectors
T0 = vecE, _, _
!
memberchkOp, plus, neg
→
true
else
Op ≡ bnot
5242unary_operandOp, T0, T ←
~z is GNU's conj
!
memberchkOp, plus, neg, bnot
5244unary_operandOp, T0, T ←
memberchkOp, plus, neg
5246unary_operandbnot, T0, T ←
5247unary_operandnot, T0, int ←
expression_binary_type+Op, +TA, +TB, -T

binary Op on operands of value types TA and TB has type T; && and || give an int (6.5.13-14).

5251expression_binary_typeOp, A, B, int ←
6.5.13p2, 6.5.14p2
memberchkOp, land, lor
!
5254expression_binary_typeOp, A, B, T ←
binary_operandsOp, A, B, _, _, T
vector_operands+Op, +TA, +TB, -C, -Tsemidet
Opina binary operator other than && and ||
TAinthe left operand's value type
TBinthe right operand's value type, one of the two a vector
Coutthe vector type both operands convert to
Toutthe result's type

GNU vector operators apply element by element. Both operands are vectors of the same element type and count, or one is a scalar, which converts to the element type and is repeated (an integer scalar for an integer vector). %, &, |, ^, << and >> need integer elements. A comparison gives a vector of signed integers as wide as the elements, each -1 where it holds and 0 where it does not.

GCC manual, Using Vector Instructions through Built-in FunctionsClang Language Extensions, Vector Operations
examples 7/7 hold
✓vector_operandsadd, vecint, 4, 16, vecint, 4, 16, C, T⟹C = vecint, 4, 16, T = vecint, 4, 16
✓vector_operandsshl, vecshort, 8, 16, int, C, _⟹C = vecshort, 8, 16
✓vector_operandseq, vecfloat, 4, 16, vecfloat, 4, 16, _, T⟹T = vecint, 4, 16
✓vector_operandslt, vecuchar, 16, 16, vecuchar, 16, 16, _, T⟹T = vecschar, 16, 16
✓vector_operandsadd, vecint, 4, 16, vecshort, 8, 16, _, _⟹fails
✓vector_operandsand, vecfloat, 4, 16, vecfloat, 4, 16, _, _⟹fails
✓vector_operandsadd, vecint, 4, 16, double, _, _⟹fails
5279vector_operandsOp, TA, TB, C, T ←
TA = vecE, N, _
TB = vecE2, N2, _
→
E ≡ E2
N ≡ N2
C = TA
TA = vecE, _, _
→
C = TA
else
TB = vecE, _, _
C = TB
C = vecE, N, A
→
type_size_alignE, _, Size, _
memberchkSize−SE, 1−schar, 2−short, 4−int, 8−llong
T = vecSE, N, A
→
callDomain, E
T = C
else
memberchkOp, shl, shr
T = C
5294vector_scalarE, S ←
5295vector_scalar_, S ←
binary_operands+Op, +TA, +TB, -CA, -CB, -Tsemidet
Opina binary operator, not && or ||
TAinthe value type of the left operand
TBinthe value type of the right operand
CAoutthe type the left operand converts to
CBoutthe type the right operand converts to
Toutthe type of the result

A shift promotes each operand on its own and has the left one's type. Other arithmetic operands take their usual arithmetic type, the result's type unless Op compares; %, &, ^ and | need integers. A pointer and an integer add, or the pointer subtracts the integer, as the pointer and a long count of elements; two pointers subtract to a long count. A comparison with a pointer compares both operands as addresses, ulong. Fails if Op does not apply.

examples 8/8 hold
✓binary_operandsshl, char, ullong, CA, CB, T⟹CA = int, CB = ullong, T = int
✓binary_operandsadd, int, uint, CA, CB, T⟹CA = uint, CB = uint, T = uint
✓binary_operandslt, int, double, CA, CB, T⟹CA = double, CB = double, T = int
✓binary_operandsadd, char, ptrint, CA, CB, T⟹CA = long, CB = ptrint, T = ptrint
✓binary_operandssub, ptrint, ptrint, _, _, T⟹T = long
✓binary_operandseq, ptrint, int, CA, CB, T⟹CA = ulong, CB = ulong, T = int
✓binary_operandsmod, double, int, _, _, _⟹fails
✓binary_operandssub, int, ptrint, _, _, _⟹fails
5330binary_operandsOp, TA, TB, C, C, T ←
TA = vec_, _, _
or
TB = vec_, _, _
!
vector_operandsOp, TA, TB, C, T
5333binary_operandsOp, TA, TB, CA, CB, T ←
6.3.1.8p1, G.5
!
memberchkOp, add, sub, mul, div, eq, ne
complex_sideTA, R, CA
complex_sideTB, R, CB
memberchkOp, eq, ne
→
T = int
else
5340binary_operandsOp, TA, TB, CA, CB, CA ←
memberchkOp, shl, shr
!
5343binary_operandsOp, TA, TB, C, C, T ←
callDomain, TA
callDomain, TB
!
→
T = int
else
T = C
5348binary_operandsOp, ptrP, TB, ptrP, long, ptrP ←
memberchkOp, add, sub
!
5350binary_operandsadd, TA, ptrP, long, ptrP, ptrP ←
5351binary_operandssub, ptrP, ptr_, ptrP, ptrP, long ←
!
5352binary_operandsOp, TA, TB, ulong, ulong, int ←
TA = ptr_
or
TB = ptr_
!
arithmetic_operator?Op, ?Domain

binary Op applies to two operands of Domain types, arithmetic or integer.

5357arithmetic_operatorOp, Domain ←
c_operator_, _, Op, Kind
operand_domainKind, Domain
operand_domain
5361arithintegerint_type
5362aritharithmeticarithmetic_type
5363comparearithmetic_type
5365comparisonOp ←
c_operator_, _, Op, compare
complex_real+T, -R

R is complex T's real type, or arithmetic T. complex_side(+T, +R, -C): an operand of type T is converted to C, the complex of the common real type R if T is complex, else R: a real operand stays real (Annex G.5.1), so 2 * z multiplies each part.

5371complex_realT, R ←
→
R = F
else
5372complex_sideT, R, C ←
else
C = R
castable+From, +Tosemidet
Frominthe unqualified value type of the operand
Tointhe type named in the cast

Anything casts to void, and a scalar to a scalar, but a floating value not to or from a pointer. As in clang and GCC, a struct or union may also be cast to its own type.

A GNU vector casts to another of the same size, reinterpreting its bytes.

C11 6.5.4p2C11 6.5.4p4GCC manual, Using Vector Instructions through Built-in Functions
examples 8/8 hold
✓castablestruct1, void
✓castableptrchar, long
✓castablestruct1, struct1
✓castablevecfloat, 4, 16, vecllong, 2, 16
✓castablevecfloat, 4, 16, int⟹fails
✓castabledouble, ptrint⟹fails
✓castableptrint, float⟹fails
✓castableint, struct1⟹fails
5394castable_, void ←
!
5396castableA, B ←
5397castableptr_, B ←
B = ptr_
or
!
5398castableA, ptr_ ←
5399castableT, U ←
T ≡ U
T = struct_
or
T = union_
5400castablevecE, N, _, vecF, M, _ ←
GNU vectors
5403vector_bytesE, N, B ←
type_size_alignE, _, S, _
B is S × N
assignable+From, +Null, +Tosemidet
Frominthe value type of the right operand
Nullinnull if the right operand is a null pointer constant, else value
Tointhe unqualified type of the object assigned

A value of type From may be assigned to an object of type To, and so passed for a parameter of type To, returned from a function returning To, or initialize a To. Integers and pointers do not mix, but for a null pointer constant or a pointer tested as a bool, and function pointers must be compatible. Clang only warns about the other pointer pairs (incompatible object types, lost qualifiers, object and function pointers), and they are accepted.

GNU vectors assign when their element type and count agree, or, as clang's default -flax-vector-conversions=integer allows, when both hold integers and have the same size (immintrin.h's __m128i from a __v16qi).

C11 6.5.16.1p1Clang command line reference, -flax-vector-conversions
examples 9/9 hold
✓assignabledouble, value, char
✓assignablevecchar, 16, 16, value, vecllong, 2, 1
✓assignablevecfloat, 4, 16, value, vecint, 4, 16⟹fails
✓assignableptrint, value, bool
✓assignableint, null, ptrint
✓assignableptrint, value, ptrchar
✓assignableint, value, ptrint⟹fails
✓assignableptrfuncint, [], fixed, value, ptrfuncvoid, [], fixed⟹fails
✓assignablestruct1, value, struct2⟹fails
5432assignableFrom, Null, To ←
assignableFrom, Null, To, none
assignable+From, +Null, +To, +Env

as assignable/3; function pointers are compared with compatible_type/3 in Env.

5436assignableFrom, _, To, _ ←
5437assignableFrom, _, To, _ ←
5438assignableT, _, U, _ ←
T ≡ U
!
5439assignableS, _, atomicS1, _, _ ←
S ≡ S1
!
5440assignablevecE, N, _, _, vecF, M, _, _ ←
!GNU vectors
E ≡ F
N ≡ M
→
true
5444assignableptr_, _, bool, _ ←
!
5445assignable_, null, ptr_, _ ←
!
5446assignableptrA, _, ptrB, Env ←
A = func_, _, _
B = func_, _, _
→
else
true
complex_or_arithmetic+A, +B

A and B are complex or arithmetic, and one is complex: a value of one converts to the other (6.3.1.7).

5451complex_or_arithmeticA, B ←
conditional_type+Arms, +Env, -Tsemidet
ArmsinA-B, the second and third operands of ?:
Envinthe scope they are read in
Toutthe type of the result

From the arms' value types; fails if the arms are incompatible or cannot be typed.

examples 3/3 hold
✓
bind_ordinaryp, varnone, ptrchar, E0, E
conditional_typechr0−idp, E, T
⟹T = ptrchar
✓
bind_ordinaryp, varnone, ptrchar, E0, E
conditional_typechr1−idp, E, T
⟹T = ptrchar
✓
bind_ordinaryp, varnone, ptrchar, E0, E1
bind_ordinaryd, varnone, double, E1, E
conditional_typeidd−idp, E, _
⟹fails
5473conditional_typeA−B, Env, T ←
armsA, B, Env, _, _, T
arms+A, +B, +Env, -XA, -XB, -T

A and B, elaborated as XA and XB, used for their values, are ?: arms giving the result type T.

5477armsA, B, Env, XA, XB, T ←
operandA, Env, XA, TA
operandB, Env, XB, TB
arm_nullnessTB, XA, Env, NA
arm_nullnessTA, XB, Env, NB
arms_typeTA, NA, TB, NB, T
arm_nullness+Other, +XV, +Env, -N

N is arm XV's nullness/3 when the other arm has the pointer type Other, the only case arms_type/5 reads it; else value. Folding every arm made nested ?: (gnulib's intprops) fold each subexpression once per enclosing ?:.

5486arm_nullnessptr_, XV, Env, N ←
!
nullnessXV, Env, N
5487arm_nullness_, _, _, value
assigned+E, +Env, +To, +Locdet
Einan expression that can be typed
Envinthe scope it is read in
Tointhe type of the object its value initializes
Locinwhere E is, for the diagnostic

E's value may be assigned to a To, as a return value or an initializer is; else the error names the two types. Arguments and assignment sources are checked where their calls and assignments elaborate.

examples 2/2 hold
✓
assignedchr0, E, ptrint, none
✓
assignedchr1, E, double, none
5502assignedE, Env, To, Loc ←
elaborateE, Env, X
valueX, XV
loweredXV, T
nullnessXV, Env, N
assignableT, N, To, Env
→
true
else
sema_errorLoc, 'cannot assign ~w to ~w', T, To
nullness+XV, +Env, -N

value tree XV is a null pointer constant (null) or not (value).

5511nullnessXV, Env, N ←
!
N = null
5512nullness_, _, value
null_pointer_constant+XV, +Envsemidet
XVina typed tree, used for its value
Envinthe scope it was elaborated in

XV is an integer constant expression of value 0, or one cast to void * (unqualified void).

examples 5/5 hold
✓
operandbinsub, chr7, chr7, E, XV, _
✓
operandcasttypetspecvoid, ptrbase, [], chr0, E, XV, _
✓
operandcasttypequalconst, tspecvoid, ptrbase, [], chr0, E, XV, _
⟹fails
✓⟹fails
✓
bind_ordinaryx, varnone, int, E0, E
operandbinmul, idx, chr0, E, XV, _
⟹fails
5533null_pointer_constantXV, Env ←
loweredXV, T
foldedXV, Env, 0
5535null_pointer_constantvalptrvoid, cast_, XV, Env ←
arms_type+TA, +NA, +TB, +NB, -Tsemidet
TAinthe value type of the second operand of ?:
NAinnull if it is a null pointer constant, else value
TBinthe value type of the third operand
NBinnull if it is a null pointer constant, else value
Toutthe type of the result

Arithmetic arms take their usual arithmetic type, like structs or unions their own. A null pointer constant takes the other arm's pointer type. Pointer arms point to the composite of their targets, qualified with both arms' qualifiers, or to void if either does. As in clang and GCC, a void arm makes the result void whatever the other arm, and incompatible pointer arms give void *. A pointer and an integer that is not a null pointer constant violate 6.5.15p3; clang and GCC only warn, and take the pointer type, converting the integer as a cast would (Ruby's error.c, RBOOL(c ? a == b : f())).

examples 12/12 hold
✓arms_typechar, value, short, value, T⟹T = int
✓arms_typevoid, value, int, value, T⟹T = void
✓arms_typeptrint, value, int, null, T⟹T = ptrint
✓arms_typeint, null, ptrchar, value, T⟹T = ptrchar
✓arms_typeptrqualifiedconst, int, value, ptrvoid, value, T⟹T = ptrqualifiedconst, void
✓arms_typeptrarrayint, none, value, ptrarrayint, 3, value, T⟹T = ptrarrayint, 3
✓arms_typeptrint, value, ptrlong, value, T⟹T = ptrvoid
✓arms_typeptrint, value, long, value, T⟹T = ptrint
✓arms_typeuchar, value, ptrchar, value, T⟹T = ptrchar
✓arms_typeptrint, value, double, value, _⟹fails
✓arms_typefloat, value, ptrint, value, _⟹fails
✓arms_typestruct1, value, struct2, value, _⟹fails
5571arms_typeTA, _, TB, _, T ←
5574arms_typeTA, _, TB, _, T ←
a complex arm (6.3.1.8p1)
!
5578arms_typevoid, _, _, _, void ←
!
5579arms_type_, _, void, _, void ←
!
5580arms_typeT, _, U, _, T ←
T ≡ U
T = struct_
or
T = union_
!
5581arms_typeptrP, _, _, null, ptrP ←
!
5582arms_type_, null, ptrP, _, ptrP ←
!
5583arms_typeptrP, _, I, _, ptrP ←
5584arms_typeI, _, ptrP, _, ptrP ←
5585arms_typeptrA, _, ptrB, _, ptrT ←
targetA, QA, A1
targetB, QB, B1
appendQA, QB, Qs
pointed_typeA1, B1, Qs, T
target+T, -Qs, -U

pointer target T is U qualified with Qs.

5590targetqualifiedQs, U, Qs, U ←
!
5591targetU, [], U
pointed_type+A, +B, +Qs, -T

pointer arms to unqualified A and B point to T, qualified with Qs (6.5.15p6).

5595pointed_typeA, B, Qs, T ←
A ≡ void
or
B ≡ void
!
qualifyQs, void, T
5596pointed_typeA, B, Qs, T ←
!
qualifyQs, C, T
5597pointed_type_, _, _, void
builtin_constant_p+E, +Env, -Vdet
Einthe operand of __builtin_constant_p, unevaluated
Envinthe environment the call is in
Vout1 if clang folds E to a constant, else 0

__builtin_constant_p(E) is an integer constant expression of type int, so __builtin_choose_expr and array sizes can test it (gnulib's string-desc.h picks a const or a mutable overload by it, systemd's CONST_MAX and LOG2U a constant form). It is 1 for an arithmetic constant expression and, as clang's evaluator gives it, for what folds to a pointer with no object (an integer constant cast to a pointer) or to the start of a string literal, under any casts, and for an element of a string literal read by * or a constant index within it. Anything else is 0: a variable, even one the program could fold, an address of an object, or a literal plus an offset.

GCC manual, Other Built-in Functions, __builtin_constant_pclang, lib/AST/ExprConstant.cpp, EvaluateBuiltinConstantP
examples 9/9 hold
✓
builtin_constant_pbinadd, num3, none−dec, num4, none−dec, E, V
⟹V = 1
✓⟹V = 1
✓
builtin_constant_pcasttypetspeclong, base, str"x", E, V
⟹V = 1
✓
builtin_constant_pcasttypetspecvoid, ptrbase, [], num0, none−dec, E, V
⟹V = 1
✓
builtin_constant_pindexstr"ab", num2, none−dec, E, V
⟹V = 1
✓
builtin_constant_pindexstr"ab", num3, none−dec, E, V
⟹V = 0
✓
builtin_constant_pbinadd, str"ab", num1, none−dec, E, V
⟹V = 0
✓
bind_ordinaryn, varl0, int, E0, E
⟹V = 0
✓
bind_ordinaryn, enumconst4, int, E0, E
⟹V = 1
5627builtin_constant_pE, Env, V ←
catchconstant_valueE, Env, _, _, not_constant, fail
→
V = 1
→
V = 1
E = underef, S
I = num0, none−dec
or
E = indexS, I
catchconst_valueI, Env, K, _, not_constant, fail
K ≥ 0
K ≤ N
→
V = 1
else
V = 0
constant_pointer+E, +Env

E, under casts, is a string literal or an integer constant: clang's pointer with no object or a literal's start.

5639constant_pointercast_, E, Env ←
!
→
true
else
catchconst_valueE, Env, _, _, not_constant, fail
5643constant_pointerE, _ ←
literal_length+E, -N

E, under casts, is a string literal of N characters before its terminating null.

5647literal_lengthcast_, E, N ←
5648literal_lengthstrBytes, N ←
lengthBytes, N
5649literal_lengthwstr_, Codes, N ←
lengthCodes, N
type_class_number+T, -Nsemidet
Tinthe unqualified type of the operand of __builtin_classify_type
NoutGCC's type class of T after value conversion

__builtin_classify_type(E) is an integer constant expression of type int, E unevaluated: 0 for void, 1 for integer types (char and, in C, an enum among them), 4 for bool, 5 for pointers, which arrays and functions decay to, 8 for the real floating types, 12 for a struct and 13 for a union (GCC's typeclass.h numbering, as clang's EvaluateBuiltinClassifyType gives it). Other types (complex, vectors) are not classified.

GCC manual, Other Built-in Functions, __builtin_classify_typeclang, lib/AST/ExprConstant.cpp, EvaluateBuiltinClassifyType
examples 8/8 hold
✓type_class_numberuchar, 1
✓type_class_numberbool, 4
✓type_class_numberarrayint, 3, 5
✓type_class_numberfuncint, [], fixed, 5
✓type_class_numberldouble, 8
✓type_class_numberunion7, 13
✓type_class_numbervoid, 0
✓type_class_numberbadx, _⟹fails
5672type_class_numbervoid, 0
5673type_class_numberbool, 4 ←
!
5674type_class_numberT, 1 ←
5675type_class_numberT, 8 ←
5676type_class_numberptr_, 5
5677type_class_numberarray_, _, 5
5678type_class_numberfunc_, _, _, 5
5679type_class_numberstruct_, 12
5680type_class_numberunion_, 13
types_compatible_value+TA, +TB, +Env, -Vdet
TAina type name
TBinanother
Envinthe scope
Vout1 if their types are compatible, ignoring top-level qualifiers, else 0

GCC's __builtin_types_compatible_p(TA, TB), an integer constant expression of type int, by compatible_type/2 on the reflected types: a typedef is its type, an enum is compatible with its underlying type but not with another enum, and char, signed char and unsigned char are three types, as are long and long long.

GCC manual, Other Built-in Functions, __builtin_types_compatible_pC11 6.2.7p1
examples 4/4 hold
✓
types_compatible_valuetypequalconst, tspecint, base, typetspecint, base, E, V
⟹V = 1
✓
types_compatible_valuetypetspeclong, base, typetspeclong, tspeclong, base, E, V
⟹V = 0
✓
types_compatible_valuetypetspecint, arraybase, none, typetspecint, arraybase, num3, none−dec, E, V
⟹V = 1
✓
types_compatible_valuetypetspecint, ptrbase, [], typequalconst, tspecint, ptrbase, [], E, V
⟹V = 0
5711types_compatible_valueTA, TB, Env, V ←
maplistcompared_type_nameEnv, TA, TB, A, B
→
V = 1
else
V = 0
5715compared_type_nameEnv, TN, T ←
type_name_typeTN, T0, Env, _
reflect_typeTN, T0, Env, R
R = qualifiedQs, R1
→
include=enum_, Qs, Es
qualifyEs, R1, T
else
T = R
lock_free+Name, +Size, +XP, +PT, +Env, -Vsemidet
Namein__atomic_always_lock_free, __atomic_is_lock_free or __c11_atomic_is_lock_free
Sizeinthe constant object size
XPinthe pointer argument's value tree
PTinits unqualified type
Envinthe scope
Vout1 or 0, the constant clang folds the call to

filcc's x86-64 target inlines atomics of up to 16 bytes (cmpxchg16b), so its clang folds as here. A power-of-two Size up to 16 is lock-free if it is 1, the pointer is a null pointer constant, the builtin is __c11_atomic_is_lock_free, or the pointer's complete pointee type is at least Size-aligned. Otherwise __atomic_always_lock_free is 0 and the others are not constant (a libatomic call, which fails here). liburcu asserts _Static_assert(__atomic_always_lock_free(sizeof(*p), 0), ...).

GCC manual, __atomic BuiltinsClang, ExprConstant.cpp: __atomic_always_lock_free and __atomic_is_lock_free
examples 4/4 hold
✓
lock_free'__c11_atomic_is_lock_free', 16, valptrvoid, p, ptrvoid, E, V
⟹V = 1
✓
lock_free'__atomic_always_lock_free', 8, valptrlong, p, ptrlong, E, V
⟹V = 1
✓
lock_free'__atomic_always_lock_free', 16, valptrlong, p, ptrlong, E, V
⟹V = 0
✓
lock_free'__atomic_always_lock_free', 32, valptrlong, p, ptrlong, E, V
⟹V = 0
5744lock_freeName, Size, XP, PT, Env, V ←
Size > 0
Size /\ Size−1 =:= 0
Size ≤ 16
Size =:= 1
→
true
Name ≡ '__c11_atomic_is_lock_free'
→
true
→
true
else
PT = ptrT
T ≢ void
catchtype_size_alignT, Env, _, A, bad_, fail
A ≥ Size
→
V = 1
else
Name ≡ '__atomic_always_lock_free'
V = 0
hint_builtin+Name, +Args, -Esemidet
Nameina builtin's name
Argsinits arguments
Eoutthe expression it evaluates, for its side effects

A hint that changes no result: __builtin_assume(C) evaluates nothing, as clang's does not; __builtin_prefetch(P, ...) evaluates P and touches no memory, so a wild address is no access; x86's __builtin_ia32_pause, the spin-wait hint of _mm_pause, does nothing.

Clang Language Extensions, __builtin_assumeGCC manual, Other Built-in Functions, __builtin_prefetch
examples 2/2 hold
✓hint_builtin'__builtin_assume', idx, num0, none−dec
✓hint_builtin'__builtin_prefetch', idp, num0, none−dec, idp
hint_builtin
5769'__builtin_assume'_num0, none−dec
5770'__builtin_prefetch'P|_P
5771'__builtin_ia32_pause'[]num0, none−dec

Generic selection

generic_selection+ControlType, +Associations, +Env, +Loc, -Selecteddet
ControlTypeinthe controlling expression's type after lvalue, array and function conversion (no integer promotion)
Associationsinthe associations, assoc(TypeName, E) or assoc(default, E)
Envinthe scope
Locinwhere the _Generic is, for errors
Selectedoutthe expression of the association whose type is compatible with ControlType, else the default's

Only Selected is evaluated, and it keeps its value category. Association types must be unique, even those that do not match; a duplicate or no match is an error (sema_error/3).

5786generic_selectionControlType, Associations, Env, Loc, Selected ←
maplistgeneric_associationEnv, Loc, Associations, Typed
generic_uniqueTyped, Loc
memberassocT, E, Typed
T ≢ default
compatible_typeControlType, T
→
Selected = E
memberchkassocdefault, E, Typed
→
Selected = E
else
sema_errorLoc, '_Generic has no matching association', []
5795generic_association_, _, assocdefault, E, assocdefault, E ←
!
5796generic_associationEnv, Loc, assocTN, E, assocT, E ←
type_name_typeTN, T0, Env, _
T0 ≠ func_, _, _
→
true
else
sema_errorLoc, '_Generic association requires a complete object type', []
reflect_typeTN, T0, Env, T
5804generic_unique[], _
5805generic_uniqueassocT, _|Rest, Loc ←
memberassocU, _, Rest
→
sema_errorLoc, '_Generic has duplicate compatible associations', []
else

Diagnoses

When an expression cannot be typed, fault/4 says why: it finds the first operand, in the order lowering evaluates them, that cannot be typed, and the rule it breaks. The messages are lowering's, which the reject tests hold to. Where lowering would first refuse something the elaborator accepts (a type nanofil does not support, a struct or union passed by value, a va_arg or a statement expression), fault/4 declines rather than misplace the blame, and lowering reports.

elaborate+E, +Env, +Loc, -Xsemidet
Eina full expression
Envinthe scope it is read in
Locinwhere it is, for a diagnostic
Xoutits typed tree

elaborate/3, raising the diagnosis at Loc when E cannot be typed. Fails, raising nothing, when fault/4 declines to say why.

examples 2/2 hold
✓
elaboratechr1, E, here, X
⟹X = valint, int1
✓
catchelaborateidg, E, here, _, nanofil_errorM, true
⟹M ≡ 'here: undeclared identifier g'
5834elaborateE, Env, Loc, X ←
elaborateE, Env, X0
→
X = X0
faultE, Env, Format, Args
→
sema_errorLoc, Format, Args
fault+E, +Env, -Format, -Argssemidet
Einan expression that cannot be typed
Envinthe scope it is read in
Formatoutthe diagnostic's format
Argsoutits arguments, with types unqualified as lowering has them

The first fault in E, in evaluation order, or first a type in E that nanofil does not support. Fails when it is not one the elaborator diagnoses.

examples 8/8 hold
✓
faultbinadd, chr1, idg, E, F, A
⟹F = 'undeclared identifier ~w', A = g
✓
bind_ordinaryx, varnone, double, E0, E1
bind_ordinaryqtypex, qualifiedconst, double, E1, E
faultbinmod, idx, chr2, E, F, A
⟹F = 'invalid operands to ~w: ~w and ~w', A = mod, double, int
✓
bind_ordinaryp, varnone, ptrint, E0, E1
bind_ordinaryd, varnone, double, E1, E
faultcondidg, idp, idd, E, F, A
⟹F = 'incompatible ?: operands ~w and ~w', A = ptrint, double
✓
bind_ordinaryx, varnone, int, E0, E
faultarrowidx, f, E, F, _
⟹F = '-> on a non-pointer'
✓
bind_ordinaryc, varnone, int, E0, E1
bind_ordinaryqtypec, qualifiedconst, int, E1, E
faultassign=, idc, idg, E, F, A
⟹F = '~w a read-only lvalue', A = 'assignment to'
✓
bind_ordinaryf, fnfuncvoid, ptrchar, int, fixed, f, extern, E0, E
faultcallidf, chr0, chr1, idg, E, F, A
⟹F = 'too many arguments to ~w', A = f
✓
bind_ordinaryf, fnfuncvoid, ptrchar, int, fixed, f, extern, E0, E
faultcallidf, chr1, idg, E, F, A
⟹F = 'cannot assign ~w to ~w', A = int, ptrchar
✓
bind_ordinarys, varnone, struct'builtin.va_list', E0, E
faultbinlor, chr0, ids, E, F, A
⟹F = 'a ~w value is not a scalar', A = struct'builtin.va_list'
5876faultE, Env, Format, Args ←
unsupportedE, Env, F, As
→
Format = F
Args = As
else
fault_E, Env, Format, Args
5881fault_idName, _, 'undeclared identifier ~w', Name
5882fault_label_addrName, _, 'the address of label ~w outside its function', Name
5883fault_indexA, I, Env, F, As ←
fault_underef, binadd, A, I, Env, F, As
5884fault_unOp, E, Env, F, As ←
untypedE, Env
→
fault_E, Env, F, As
Op ≡ deref
→
F = 'dereferencing a non-pointer'
As = []
Op ≡ addr
elaborateE, Env, obj_, bitfield_, _
→
F = 'cannot take the address of a bit-field'
As = []
Op ≡ addr
→
F = '& of a non-lvalue'
As = []
memberchkOp, plus, neg, bnot, not
→
lowered_typeE, Env, T
F = 'invalid operand to ~w: ~w'
As = Op, T
elaborateE, Env, X
¬modifiableX, Env
→
atom_concatOp, ' of', What
modifiable_faultX, What, F, As
else
F = '~w of a non-scalar lvalue'
As = Op
5896fault_memberE, Field, Env, F, As ←
untypedE, Env
→
fault_E, Env, F, As
→
missing_memberT, Field, Env, F, As
else
F = 'member access on a non-lvalue'
As = []
5902fault_real_partE, Env, F, As ←
complex_part_fault'__real__', E, Env, F, As
5903fault_imag_partE, Env, F, As ←
complex_part_fault'__imag__', E, Env, F, As
5904fault_arrowE, Field, Env, F, As ←
untypedE, Env
→
fault_E, Env, F, As
lowered_typeE, Env, ptrT
→
missing_memberT, Field, Env, F, As
else
F = '-> on a non-pointer'
As = []
5909fault_castTN, E, Env, F, As ←
type_name_typeTN, To, Env, _
untypedE, Env
→
fault_E, Env, F, As
else
lowered_typeE, Env, From
F = 'cannot convert ~w to ~w'
As = From, To
5914fault_bit_castTN, E, Env, F, As ←
type_name_typeTN, To, Env, _
untypedE, Env
→
fault_E, Env, F, As
else
lowered_typeE, Env, From
F = '__builtin_bit_cast from ~w to ~w, which is not an object of the same size that is not an array'
As = From, To
5921fault_callidName, _, Env, F, Name ←
¬lookup_ordinaryEnv, Name, _
!
→
F = '~w needs a pointer to an integer (an _Atomic one for __c11 builtins), or to a pointer to exchange, compare it or (__c11) add to it, and constant memory orders'
→
F = '~w needs a constant memory order'
atom_concat'__c11', _, Name
→
F = '~w needs a pointer to an _Atomic integer, pointer, float or double and a constant memory order it supports (relaxed, consume, acquire or seq_cst for a load; relaxed, release or seq_cst for a store)'
else
F = '~w needs a pointer to an integer or pointer and a constant memory order it supports (relaxed, consume, acquire or seq_cst for a load; relaxed, release or seq_cst for a store)'
5931fault_callidName, _, Env, F, Name, Chk, Plain ←
¬lookup_ordinaryEnv, Name, _
checking_builtinName, Chk, Plain
!
F = '~w needs ~w declared, or ~w declared and an object size of (size_t)-1'
5934fault_callFn, Args, Env, F, As ←
¬
Fn = idName
¬lookup_ordinaryEnv, Name, _
or
float_builtinName, _, _
untypedFn, Env
→
fault_Fn, Env, F, As
¬lowered_typeFn, Env, ptrfunc_, _, _
→
F = 'called object is not a function'
As = []
else
lowered_typeFn, Env, ptrfunc_, Ps, V
callee_nameFn, Env, Sym
arity_faultPs, V, Args, Sym, F0, As0
→
F = F0
As = As0
else
arguments_faultPs, Args, Env, F, As
5946fault_assignOp, A, B, Env, F, As ←
untypedA, Env
→
fault_A, Env, F, As
elaborateA, Env, X
¬modifiableX, Env
→
modifiable_faultX, 'assignment to', F, As
else
elaborateA, Env, X
Op ≡ (=)
→
true
untypedB, Env
→
fault_B, Env, F, As
else
lowered_typeB, Env, TB
assignment_faultOp, TA, TB, F, As
5956fault_commaA, B, Env, F, As ←
operands_faultA, B, Env, F, As
5957fault_alignof_exprE, Env, F, As ←
fault_E, Env, F, As
5958fault_sizeof_exprE, Env, F, As ←
untypedE, Env
→
fault_E, Env, F, As
else
F = 'sizeof of a bit-field'
As = []
5962fault_binOp, A, B, Env, F, As ←
memberchkOp, land, lor
!
scalar_faultA, Env, F, As
→
true
else
scalar_faultB, Env, F, As
5967fault_binOp, A, B, Env, F, As ←
operands_faultA, B, Env, F, As
→
true
else
maplistlowered_type_inEnv, A, B, TA, TB
F = 'invalid operands to ~w: ~w and ~w'
As = Op, TA, TB
5972fault_condC, A, B, Env, F, As ←
arms_faultA, B, Env, F, As
→
true
else
scalar_faultC, Env, F, As
5976fault_gcondC, B, Env, F, As ←
arms_faultC, B, Env, F, As
→
true
else
scalar_faultC, Env, F, As
5980fault_genericC, As0, Env, F, As ←
untypedC, Env
→
fault_C, Env, F, As
else
operandC, Env, _, CT
generic_selectionCT, As0, Env, constant, E
fault_E, Env, F, As
arms_fault+A, +B, +Env, -F, -As

the first fault of ?: arms A and B, lowered first, then whether they go together (6.5.15p3).

5989arms_faultA, B, Env, F, As ←
operands_faultA, B, Env, F, As
→
true
else
¬conditional_typeA−B, Env, _
maplistlowered_type_inEnv, A, B, TA, TB
F = 'incompatible ?: operands ~w and ~w'
As = TA, TB
operands_fault+Es, +Env, -F, -As

the fault of the first of Es that cannot be typed.

5998operands_faultEs, Env, F, As ←
memberE, Es
untypedE, Env
!
fault_E, Env, F, As
6002untypedE, Env ←
¬elaborateE, Env, _
scalar_fault+E, +Env, -F, -As

E, tested for truth, cannot be typed or is not a scalar.

6006scalar_faultE, Env, F, As ←
untypedE, Env
→
fault_E, Env, F, As
else
lowered_typeE, Env, T
F = 'a ~w value is not a scalar'
As = T
modifiable_fault+X, +What, -F, -As

X, the operand of What, is not a modifiable lvalue.

6014modifiable_faultobj_, _, What, '~w a read-only lvalue', What ←
!
6015modifiable_fault_, What, '~w a non-lvalue', What
assignment_fault+Op, +TA, +TB, -F, -As

why assignment Op cannot store a value of type TB into an object of type TA.

6019assignment_fault=, TA, TB, 'cannot assign ~w to ~w', TB, TA ←
!
6020assignment_faultOp, TA, TB, F, As ←
binary_operandsBinOp, TA, TB, _, _, T
→
F = 'cannot assign ~w to ~w'
As = T, TA
else
F = 'invalid operands to ~w: ~w and ~w'
As = BinOp, TA, TB
callee_name+Fn, +Env, -Sym

lowering names a call's callee by its symbol when Fn names a function, else as a function pointer.

6029callee_nameidName, Env, Sym ←
lookup_ordinaryEnv, Name, fn_, Sym, _
!
6030callee_name_, _, 'function pointer'
arity_fault+Ps, +V, +Args, +Sym, -F, -As

Args are too few or too many for parameters Ps of callee Sym.

6034arity_faultPs, V, Args, Sym, F, Sym ←
Ps ≢ unspecified
lengthPs, NP
lengthArgs, NA
NA < NP
→
F = 'too few arguments to ~w'
NA > NP
V ≡ fixed
→
F = 'too many arguments to ~w'
arguments_fault+Ps, +Args, +Env, -F, -As

the first argument, in order, that cannot be typed or passed for its parameter. Lowering may refuse a struct or union argument, or an unpromotable variadic one, before it reaches a later one, so those end the search.

6044arguments_faultPs, A|Args, Env, F, As ←
param_splitPs, P, Ps1
untypedA, Env
→
fault_A, Env, F, As
else
operandA, Env, XA, _
loweredXA, T
P ≡ none
→
else
T = ptr_
arguments_faultPs1, Args, Env, F, As
else
nullnessXA, Env, N
¬parameter_acceptsTP, T, N, Env
→
F = 'cannot assign ~w to ~w'
As = T, TP
else
TP ≠ struct_
TP ≠ union_
arguments_faultPs1, Args, Env, F, As
param_split
6060unspecifiednoneunspecified
6061[]none[]
6062P|PsPPs
member_subject+XS

lowering looks for members in XS, an lvalue or a struct or union value; it refuses other values.

6066member_subjectobj_, _
6067member_subjectvalT, _ ←
U = struct_
or
U = union_
lowered_type+E, +Env, -T

T is E's value type as lowering has it, unqualified.

6071lowered_typeE, Env, T ←
operandE, Env, _, T0
6072lowered_type_inEnv, E, T ←
lowered_typeE, Env, T
unsupported+E, +Env, -F, -As

the first type name in E that names a type nanofil does not support, and why.

6076unsupportedE, Env, F, As ←
E = type_, _
!
type_name_typeE, T, Env, _
6080unsupportedE, Env, F, As ←
compoundE
E =.. _|Xs
memberX, Xs
unsupportedX, Env, F, As
!
missing_member+SU, +Field, +Env, -F, -As

the fault of naming Field in SU, which has no such usable member: when SU's definition dropped a member nanofil refuses (a vector one without vectors), the refusal's reason, so x264_union128_sse_t's __m128 i reads as vector types not being supported rather than as a missing member.

6089missing_memberT, Field, Env, F, As ←
SU = structId
or
SU = unionId
Env = e_, _, Defs, _
get_assocId, Defs, Def
Def = badWhy
or
Def = partialWhy, _
→
F = '~w is not supported (member ~w)'
As = Why, Field
else
F = 'no member named ~w'
As = Field