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.

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
list_to_assoc'builtin.va_list'−Def, Defs
109va_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.

118lookup_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.

126bind_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.

135env_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.

142env_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.

149sema_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.

165decl_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
191decl_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).

205declarator_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, Env4
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'
276object_alignment[], Type, _, Type, none ←
!
277object_alignment_, badR, _, badR, none ←
!

keep the first reason

278object_alignment_, func_, _, _, _, bad'alignment attributes', none ←
!
279object_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
322constant_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.

336max_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'
367typedef_alignment[], Inherited, _, _, A ←
!
Inherited = alignA
→
true
else
A = none
369typedef_alignmentRequests, _, _, _, bad'_Alignas on a typedef' ←
memberchkalignas_, Requests
!
371typedef_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 = []
393inherited_alignmentSpecs, SyntaxType, Env, Inherited ←
memberchktnameN, Specs
lookup_ordinaryEnv, talignN, A
integerA
→
Inherited = alignA
else
Inherited = []
400object_declaratorbase
401object_declaratorarrayS, _ ←
403innermost_elementarrayT, _, E ←
404innermost_elementT, T
request_alignment+Env, +Request, -A

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

408request_alignment_, alignasvalueA, A ←
!
409request_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
415request_alignmentEnv, alignasexprE, A ←
!
catchconst_valueE, Env, A, _, not_constant, fail
A =:= 0
→
true
else
A > 0
A /\ A−1 =:= 0
418request_alignmentEnv, attrgroupAttr, A ←
!__attribute__((...))
request_alignmentEnv, attrAttr, A
420request_alignment_, attrWord, 16 ←
memberchkWord, idaligned, id'__aligned__'
!
422request_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.

444reflect_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
451someName_Name
452noneenumeratorFirst, _|_anonFirst
453nonepackedenumeratorFirst, _|_anonFirst
455reflect_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
471reflect_declaratorptrS, Qs, Specs, ptrT, Env, R ←
reflect_declaratorS, Specs, T, Env, RT
qualifyQs, ptrRT, R
473reflect_declaratorarrayS, _, Specs, arrayT, N, Env, arrayRT, N ←
reflect_declaratorS, Specs, T, Env, RT
475reflect_declaratorfuncS, Params, Specs, funcT, Ps, V, Env, funcRT, RPs, V ←
reflect_declaratorS, Specs, T, Env, RT
reflect_param_typesParams, Ps, Env, RPs
478reflect_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.

484atomic_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
490atomic_su_base_, Base, Base
492reflect_param_typesunspecified, unspecified, _, unspecified
493reflect_param_typesparamsParams, _, Ts, Env, RTs ←
maplistreflect_param_typeEnv, Params, Ts, RTs
496reflect_param_typeEnv, paramSpecs, dcl_, Syntax, _, T, RT ←
parameter_syntaxSyntax, Adjusted
reflect_typetypeSpecs, Adjusted, T, Env, R0
501parameter_syntaxarrayS, _, ptrS, [] ←
!
502parameter_syntaxfuncS, P, ptrfuncS, P, [] ←
!
503parameter_syntaxS, S
505qualifyQs0, qualifiedQs1, T, R ←
!
appendQs0, Qs1, Qs
qualifyQs, T, R
507qualifyQs, arrayT0, N, arrayT, N ←
Qs ≢ []C11 6.7.3p9
!
qualifyQs, T0, T
509qualifyQs0, 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.

519reflect_paramsfunc_, paramsParams, _, Types, Env0, Env ←
!
foldlreflect_param, Params, Types, Env0, Env
521reflect_params_, _, Env, Env
523reflect_paramParam, T, Env0, Env ←
Param = param_, dclsomeName, _, _
!
reflect_param_typeEnv0, Param, T, RT
bind_ordinaryqtypeName, RT, Env0, Env
527reflect_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. Env is Env0 with the struct, union and enum definitions in Specs entered.

537specs_typeSpecs, Type, Env0, Env ←
base_typeSpecs, Type0, Env0, Env
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
570known_attributeWord, Kind, Why ←
attribute_entryWord, Kind, Why
572attribute_entryaligned, honored, 'c_types\' layout (object_alignment/5, member alignment)'
573attribute_entrypacked, honored, 'c_types\' layout'
574attribute_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'
575attribute_entrytransparent_union, honored, 'transparent_union/3: a parameter of the union takes any member'
576attribute_entrycleanup, honored, 'c_lower cleanup_decls//4 runs it on every scope exit'
577attribute_entrymode, honored, 'mode_type/3'
578attribute_entryvector_size, honored, 'the type is refused (bad) where it is used'
579attribute_entryext_vector_type, honored, 'as vector_size'
580attribute_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*)'
581attribute_entryweak, honored, 'c_lower weak symbols (weak_attr/1)'
582attribute_entryalias, honored, 'c_lower alias_decl/8; a variable alias is refused'
583attribute_entryweakref, honored, 'c_lower weakref_item/3: an extern reference to its target'
584attribute_entryconstructor, honored, 'c_lower init_decl//5'
585attribute_entrydestructor, honored, 'c_lower init_decl//5'
586attribute_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)'
587attribute_entrynoinline, hint, 'as always_inline'
588attribute_entryflatten, hint, 'as always_inline'
589attribute_entryartificial, hint, 'debug information only'
590attribute_entrynodebug, hint, 'debug information only'
591attribute_entrygnu_inline, honored, 'c_lower external_inline_body/4 gives the body GNU\'s extern inline rule'
592attribute_entryhot, hint, 'placement and optimization hint'
593attribute_entrycold, hint, 'placement and optimization hint'
594attribute_entryoptimize, hint, 'per-function optimization level'
595attribute_entryoptnone, hint, 'clang\'s per-function -O0, as optimize; ghostscript\'s ttinterp.c and sjpege.c'
596attribute_entrytarget, hint, 'instruction set of the function; nanofil emits baseline x86-64'
597attribute_entrymin_vector_width, hint, 'vector width hint of clang\'s intrinsic headers'
598attribute_entrynoreturn, hint, 'a promise about calls; nanofil does not rely on it'
599attribute_entryreturns_twice, hint, 'glibc marks setjmp, sigsetjmp and vfork; nanofil lowers setjmp\'s family as intrinsics by name'
600attribute_entryleaf, hint, 'an optimizer promise about calls back into the unit'
601attribute_entrynothrow, hint, 'no C++ exceptions in C'
602attribute_entrypure, hint, 'an optimizer promise; nanofil does not rely on it'
603attribute_entryconst, hint, 'an optimizer promise; nanofil does not rely on it'
604attribute_entrymalloc, hint, 'an aliasing promise about the result; nanofil does not rely on it'
605attribute_entryalloc_size, hint, 'object size hint; nanofil\'s __builtin_object_size answers unknown'
606attribute_entryalloc_align, hint, 'alignment hint about the result'
607attribute_entryalign_value, hint, 'alignment assumption about a pointer parameter (clang\'s intrinsic headers)'
608attribute_entryassume_aligned, hint, 'alignment hint about the result'
609attribute_entryreturns_nonnull, hint, 'a promise about the result'
610attribute_entrynonnull, hint, 'a promise about arguments, for diagnostics and the optimizer'
611attribute_entrynoescape, hint, 'a promise that a pointer argument does not escape; nanofil does not rely on it'
612attribute_entrynonnull_if_nonzero, hint, 'as nonnull'
613attribute_entryformat, hint, 'printf and scanf format checking'
614attribute_entryformat_arg, hint, 'format checking'
615attribute_entrysentinel, hint, 'argument checking'
616attribute_entryaccess, hint, 'argument access checking'
617attribute_entryfd_arg, hint, 'file descriptor checking'
618attribute_entryfd_arg_read, hint, 'file descriptor checking'
619attribute_entryfd_arg_write, hint, 'file descriptor checking'
620attribute_entrynull_terminated_string_arg, hint, 'argument checking'
621attribute_entrytainted_args, hint, 'static analysis only'
622attribute_entrywarn_unused_result, hint, 'a diagnostic'
623attribute_entrynodiscard, hint, 'a diagnostic'
624attribute_entrydeprecated, hint, 'a diagnostic'
625attribute_entryunavailable, hint, 'a diagnostic; a use is an error in clang too'
626attribute_entryerror, hint, 'a diagnostic at calls clang cannot remove'
627attribute_entrywarning, hint, 'a diagnostic'
628attribute_entrydiagnose_if, hint, 'a diagnostic (glibc\'s clang fortify)'
629attribute_entrydiagnose_as_builtin, hint, 'a diagnostic (glibc\'s clang fortify)'
630attribute_entryenable_if, hint, 'only on glibc\'s clang fortify overloads, which nanofil handles as overloadable'
631attribute_entryoverloadable, honored, 'an incompatible redeclaration is skipped (docs/m4.md)'
632attribute_entrypass_object_size, hint, 'glibc\'s clang fortify wrappers; nanofil\'s object sizes are unknown, so they call the plain function'
633attribute_entrypass_dynamic_object_size, hint, 'as pass_object_size'
634attribute_entryunused, hint, 'a diagnostic'
635attribute_entrymaybe_unused, hint, 'a diagnostic'
636attribute_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'
637attribute_entryretain, hint, 'as used, for the linker'
638attribute_entryexternally_visible, hint, 'whole-program optimization hint'
639attribute_entryvisibility, hint, 'dynamic export; nanofil exports every external symbol, observable only through interposition'
640attribute_entrynoipa, hint, 'optimizer hint'
641attribute_entrynoclone, hint, 'optimizer hint'
642attribute_entryno_icf, hint, 'optimizer hint'
643attribute_entryno_reorder, hint, 'optimizer hint'
644attribute_entryno_instrument_function, hint, 'profiling instrumentation'
645attribute_entryno_profile_instrument_function, hint, 'profiling instrumentation'
646attribute_entryno_sanitize, hint, 'sanitizer instrumentation'
647attribute_entryno_sanitize_address, hint, 'sanitizer instrumentation'
648attribute_entryno_sanitize_thread, hint, 'sanitizer instrumentation'
649attribute_entryno_sanitize_undefined, hint, 'sanitizer instrumentation'
650attribute_entryno_address_safety_analysis, hint, 'sanitizer instrumentation'
651attribute_entryno_stack_protector, hint, 'stack protector instrumentation'
652attribute_entrystack_protect, hint, 'stack protector instrumentation'
653attribute_entryno_split_stack, hint, 'stack splitting'
654attribute_entryzero_call_used_regs, hint, 'register clearing on return'
655attribute_entrynocf_check, hint, 'control-flow protection marking'
656attribute_entrycf_check, hint, 'control-flow protection marking'
657attribute_entryindirect_return, hint, 'control-flow protection marking'
658attribute_entrypatchable_function_entry, hint, 'padding for live patching'
659attribute_entrynoplt, hint, 'call sequence through the GOT'
660attribute_entrytls_model, hint, 'thread-local access sequence'
661attribute_entryuninitialized, hint, 'opts out of -ftrivial-auto-var-init'
662attribute_entrycommon, honored, 'c_lower weak_name/3: a tentative definition with it is weak, as filcc emits it'
663attribute_entrynocommon, hint, 'nanofil, as clang, never emits common symbols'
664attribute_entrysysv_abi, hint, 'the x86-64 calling convention nanofil uses'
665attribute_entrystdcall, hint, 'an i386 calling convention; clang ignores it on x86-64'
666attribute_entrycdecl, hint, 'an i386 calling convention; clang ignores it on x86-64'
667attribute_entryfastcall, hint, 'an i386 calling convention; clang ignores it on x86-64'
668attribute_entrythiscall, hint, 'an i386 calling convention; clang ignores it on x86-64'
669attribute_entrycapability, hint, 'clang thread safety analysis, a static check only'
670attribute_entryshared_capability, hint, 'clang thread safety analysis, a static check only'
671attribute_entrylockable, hint, 'clang thread safety analysis, a static check only'
672attribute_entryscoped_lockable, hint, 'clang thread safety analysis, a static check only'
673attribute_entryguarded_by, hint, 'clang thread safety analysis, a static check only'
674attribute_entrypt_guarded_by, hint, 'clang thread safety analysis, a static check only'
675attribute_entryguarded_var, hint, 'clang thread safety analysis, a static check only'
676attribute_entrypt_guarded_var, hint, 'clang thread safety analysis, a static check only'
677attribute_entryacquired_before, hint, 'clang thread safety analysis, a static check only'
678attribute_entryacquired_after, hint, 'clang thread safety analysis, a static check only'
679attribute_entryrequires_capability, hint, 'clang thread safety analysis, a static check only'
680attribute_entryrequires_shared_capability, hint, 'clang thread safety analysis, a static check only'
681attribute_entryexclusive_locks_required, hint, 'clang thread safety analysis, a static check only'
682attribute_entryshared_locks_required, hint, 'clang thread safety analysis, a static check only'
683attribute_entryacquire_capability, hint, 'clang thread safety analysis, a static check only'
684attribute_entryacquire_shared_capability, hint, 'clang thread safety analysis, a static check only'
685attribute_entryexclusive_lock_function, hint, 'clang thread safety analysis, a static check only'
686attribute_entryshared_lock_function, hint, 'clang thread safety analysis, a static check only'
687attribute_entryrelease_capability, hint, 'clang thread safety analysis, a static check only'
688attribute_entryrelease_shared_capability, hint, 'clang thread safety analysis, a static check only'
689attribute_entryrelease_generic_capability, hint, 'clang thread safety analysis, a static check only'
690attribute_entryunlock_function, hint, 'clang thread safety analysis, a static check only'
691attribute_entrytry_acquire_capability, hint, 'clang thread safety analysis, a static check only'
692attribute_entrytry_acquire_shared_capability, hint, 'clang thread safety analysis, a static check only'
693attribute_entryexclusive_trylock_function, hint, 'clang thread safety analysis, a static check only'
694attribute_entryshared_trylock_function, hint, 'clang thread safety analysis, a static check only'
695attribute_entryassert_capability, hint, 'clang thread safety analysis, a static check only'
696attribute_entryassert_shared_capability, hint, 'clang thread safety analysis, a static check only'
697attribute_entryassert_exclusive_lock, hint, 'clang thread safety analysis, a static check only'
698attribute_entryassert_shared_lock, hint, 'clang thread safety analysis, a static check only'
699attribute_entrylocks_excluded, hint, 'clang thread safety analysis, a static check only'
700attribute_entrylock_returned, hint, 'clang thread safety analysis, a static check only'
701attribute_entryno_thread_safety_analysis, hint, 'clang thread safety analysis, a static check only'
702attribute_entryforce_align_arg_pointer, hint, 'realigns the stack on entry; Fil-C code is always entered with the ABI\'s 16-byte alignment'
703attribute_entryanalyzer_noreturn, hint, 'clang static analyzer only'
704attribute_entryregparm, hint, 'i386 only; clang ignores it on x86-64'
705attribute_entrysimd, hint, 'declares vector variants of a math function; calls stay scalar'
706attribute_entrynonstring, hint, 'a diagnostic'
707attribute_entrycounted_by, hint, 'bounds hint for clang\'s sanitizers and object sizes; Fil-C checks every access'
708attribute_entrystrict_flex_array, hint, 'object size hint'
709attribute_entrydesignated_init, hint, 'a diagnostic'
710attribute_entryflag_enum, hint, 'a diagnostic'
711attribute_entryenum_extensibility, hint, 'a diagnostic'
712attribute_entrywarn_if_not_aligned, hint, 'a diagnostic'
713attribute_entrycopy, hint, 'copies another declaration\'s attributes, which are checked there'
714attribute_entryfallthrough, hint, 'a diagnostic, as a statement attribute'
715attribute_entrylikely, hint, 'a branch hint, as a statement attribute'
716attribute_entryunlikely, hint, 'a branch hint, as a statement attribute'
717attribute_entryassume, hint, 'a statement attribute whose expression is not evaluated'
718attribute_entrymusttail, hint, 'a statement attribute; the call means the same'
719attribute_entrynomerge, hint, 'optimizer hint'
720attribute_entryreproducible, hint, 'C23 optimizer promise'
721attribute_entryunsequenced, hint, 'C23 optimizer promise'
722attribute_entrymay_alias, hint, 'nanofil does no type-based alias analysis, so it means nothing in any position'
723attribute_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.

810mode_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 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 5/5 hold
✓mode_typeint, 'DI', long
✓mode_typeuint, 'QI', uchar
✓mode_typeint, word, long
✓mode_typedouble, 'SF', float
✓mode_typeint, 'TI', bad'mode TI (__int128)'
834mode_typebadWhy, _, badWhy ←
!
835mode_typequalifiedQs, T0, Mode, Type ←
!
mode_typeT0, Mode, T
T = bad_
→
Type = T
else
Type = qualifiedQs, T
838mode_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
849'QI'1
850byte1
851'HI'2
852'SI'4
853'DI'8
854word8
855pointer8
856'TI'16
858float_mode'SF', float
859float_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
873vector_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
898vector_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
924vector_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
!
931vector_elementT ←
T ≢ bool
932vector_elementfloat
933vector_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.

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

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

946packed_attrTokens ←
is_listTokens
attr_wordTokens, Word
memberchkWord, packed, '__packed__'
!
949aligned_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
966attr_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
978attr_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.

982base_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.

1003named_typetname'__builtin_va_list', arraystruct'builtin.va_list', 1, Env, Env ←
!
1005named_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.

1011named_typeauto_type, bad'__auto_type without an initializer', Env, Env
1012named_typetypeofArg, Type, Env0, Env ←
typeof_typeArg, Type, Env0, Env
1014named_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'
1026named_typesuKind, Tag, Members, Attrs, Type, Env0, Env ←
su_typeMembers, Kind, Tag, Attrs, Type, Env0, Env
1028named_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
1059atomic_object_typeT ←
1060atomic_object_typeptr_
1061atomic_object_typefloat
1062atomic_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).

1086atomic_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.

1093holds_refptr_, _ ←
!
1094holds_refvec_, _, _, _ ←
!
1095holds_refarrayT, _, Env ←
!
holds_refT, Env
1096holds_refatomicT, _, Env ←
!
holds_refT, Env
1097holds_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
1108atomic_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
1127typeof_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'
1134typeof_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.

1139keyword_type[], int

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

1140keyword_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.

1147keyword_combovoid, void
1148keyword_combobool, bool
1149keyword_combochar, char
1150keyword_combochar, signed, schar
1151keyword_combochar, unsigned, uchar
1152keyword_comboS, short ←
memberS, short, short, signed, int, short, int, short, signed
1154keyword_comboS, ushort ←
memberS, short, unsigned, int, short, unsigned
1156keyword_comboS, int ←
memberS, int, signed, int, signed
1158keyword_comboS, uint ←
memberS, unsigned, int, unsigned
1160keyword_comboS, long ←
memberS, long, long, signed, int, long, int, long, signed
1162keyword_comboS, ulong ←
memberS, long, unsigned, int, long, unsigned
1164keyword_comboS, llong ←
memberS, long, long, long, long, signed, int, long, long, int, long, long, signed
1167keyword_comboS, ullong ←
memberS, long, long, unsigned, int, long, long, unsigned
1169keyword_combofloat, float
1170keyword_combodouble, double
1171keyword_combodouble, long, ldouble
1172keyword_comboS, bad'_Complex' ←
memberchkcomplex, S
1173keyword_comboS, bad'__int128' ←
memberchkint128, S
1174keyword_comboS, bad'_Float16' ←
memberchkfloat16, S
1175keyword_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.

1184su_type[], Kind, Tag, Attrs, Type, Env0, Env ←
su_definition[], Kind, Tag, Attrs, Type, Env0, Env
1186su_typeMember|Members, Kind, Tag, Attrs, Type, Env0, Env ←
su_definitionMember|Members, Kind, Tag, Attrs, Type, Env0, Env
1188su_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.

1198su_definitionMembers, Kind, Tag, Attrs, Type, Env0, Env ←
Env0 = e_, _, Defs0, _
get_assocexpr_tagKind, Tag, Members, Attrs, Defs0, Id
!
Type =.. Kind, Id
Env = Env0
1202su_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.

1255expression_tagsE, Env0, Env ←
TNs0 ≡ []
→
Env = Env0
else
b_setvalnanofil_expr_tags, true
foldldefine_type_name, TNs0, Env0, Env
b_setvalnanofil_expr_tags, false
1263define_type_nameTN, Env0, Env ←
catchtype_name_typeTN, _, Env0, Env1, _, fail
!
Env = Env1
1265define_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.

1270defining_type_namesE, TNs, TNs ←
¬compoundE
!
1271defining_type_namesstmtexpr_, TNs, TNs ←
!
1272defining_type_namestypeSpecs, D, typeSpecs, D|TNs, TNs ←
is_listSpecs
membersu_, _, Ms, _, Specs
Ms ≢ none
!
1274defining_type_namesE, TNs, Rest ←
E =.. _|Args
defining_argsArgs, TNs, Rest
1278defining_args[], TNs, TNs
1279defining_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
1307definition_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).

1314aligned_entryTokens, Request ←
attr_entryTokens, Word, Rest
memberchkWord, aligned, '__aligned__'
Rest = groupTs|_
→
Request = argTs
else
Request = bare
1319requested_alignment_, bare, A0, A ←
A is maxA0, 16
1320requested_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.

1329aligned_defdefKind, Members, Size0, Align0, Request, defKind, Members, Size, Align ←
!
Align is maxAlign0, Request
Size is Size0 + Align−1//Align × Align
1332aligned_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
1349bit_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.

1357known_field_prefix[], []
1358known_field_prefixbad_|_, [] ←
!
1359known_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.

1364forward_declaredsomeName, Kind, e_, Tags, Defs, _, Id ←
get_assocName, Tags, tagKind, Id
get_assocId, Defs, incomplete_
1368new_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.

1375tag_boundnone, _, Tags, Tags
1376tag_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
1403member_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', []
1408member_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, Specs
nonvarBase
→
true
else
specs_typeSpecs, Base, Env0, Env1a
bind_ordinaryenclosingmember_base, Base, Env1a, Env1
apply_declaratorSyntaxType, Base, Type, Env1, Env
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'
1468member_alignment[], [], _, none ←
!
1469member_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'
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
1499alignment_requests[], [], []
1500alignment_requestsalignasA|Ss0, alignasA|Rs, Ss ←
!
1502alignment_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
1512alignment_requestsattrTokens|Ss0, attrTokens|Rs, Ss ←
1515alignment_requestsS|Ss0, Rs, S|Ss ←
attr_items?Tokens, ?Items

Tokens are the Items joined by commas.

1519attr_itemsTokens, I|Is ←
nonvarTokens
!
appendI, p(',')|Rest, Tokens
→
attr_itemsRest, Is
else
I = Tokens
Is = []
1524attr_itemsTokens, I ←
!
Tokens = I
1525attr_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.

1530attribute_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.

1537flat_tokens[] ⟶
[]
1538flat_tokensgroupTs|Rest ⟶
!
tokp'(', attr
tokp')', attr
1540flat_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).

1545bitfield_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'
1613su_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).

1619su_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.

1628fields_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.

1639layout_defplacedMembers, End, Align, Kind, defKind, Members, Size, Align ←
Size is End + Align−1//Align × Align
1641layout_defincomplete, _, bad'incomplete member'
1642layout_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.

1650field_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
1664field_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
1681field_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
1692field_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.

1702field_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.

1722packed_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'
1733packed_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.

1738packed_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
1760unaligned_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
!
1766unaligned_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
1789underaligned_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
1820member_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.

1830member_offset_endstruct, End0, Size, Align, Off, End ←
Off is End0 + 7//8 + Align−1//Align × Align
End is 8 × Off + Size
1833member_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.

1839bitfield_start_endunion, End0, W, _, 0, End ←
End is maxEnd0, W
1840bitfield_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.

1853enum_typepackedEnumerators, Tag, Type, Env0, Env ←
!
enum_definitionpacked, Enumerators, Tag, Type, Env0, Env
1855enum_type[], Tag, Type, Env0, Env ←
enum_definition[], Tag, Type, Env0, Env
1857enum_typeEnumerator|Enumerators, Tag, Type, Env0, Env ←
enum_definitionEnumerator|Enumerators, Tag, Type, Env0, Env
1859enum_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.

1870enum_definitionEnumerators, Tag, Type, Env0, Env ←
enum_definitionnatural, Enumerators, Tag, Type, Env0, Env
1872enum_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.

1885enumeratorenumeratorName, 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).

1901enum_underlying[], uint
1902enum_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
1926packed_enum_underlying[], uchar ←
!
1927packed_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
1967apply_declaratorbase, Base, Base, Env, Env
1968apply_declaratorptrSyntax, _, Base, ptrType, Env0, Env ←
apply_declaratorSyntax, Base, Type, Env0, Env
1970apply_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
1980apply_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.

1988param_typesunspecified, unspecified, fixed, Env, Env
1989param_typesknr_, _, _, _, _ ←
6.7.6.3p3
thrownanofil_error'a parameter list without types is only allowed in a function definition'
1991param_typesparamsParams, Variadic, Types, Variadic, Env0, Env ←
env_scopeEnv0, Scope, _
foldlparam_type, Params, Types, Env0, Env1
env_restoreScope, Env1, Env
1996param_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 = []
2035knr_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
2050knr_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
2082transparent_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
2107transparent_memberParam, T, N, Env, M ←
transparent_unionParam, Env, Ms
memberM, Ms
member_takesM, T, N, Env
!
2112member_takesptr_, ptrvoid, _, _ ←
!
2113member_takesptr_, _, null, _ ←
!
2114member_takesM, T, _, _ ←
M ≡ T
!
2115member_takesM, T, _, _ ←
2116member_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
2131union_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.

2140parameter_acceptsP, T, N, Env ←
assignableT, N, P, Env
!
2141parameter_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
2151transparent_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.

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

the attribute tokens name transparent_union.

2164transparent_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.

2171type_adjustedarrayT, _, ptrT ←
!
2172type_adjustedfuncR, P, V, ptrfuncR, P, V ←
!
2173type_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.

2180type_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
2197arith_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
2214vm_typequalified_, T ←
2215vm_typeptrT ←
2216vm_typearrayT, N ←
2217vm_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.

2227variable_sizevla_
2228variable_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
2242vla_typequalified_, T ←
2243vla_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
2255unsupported_typeT, '~w is not supported', Why ←
unsupported_value_type+Type, -Why

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

2260unsupported_value_typebadWhy, Why
2261unsupported_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).

2265bad_typebadWhy, Why
2266bad_typearrayT, _, Why ←
bad_typeT, Why
2267bad_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.

2280type_size_alignType, Env, Size, Align ←
size_align_Type, Env, Size0, Align0
→
Size = Size0
Align = Align0
else
throwbadincomplete
2285size_align_qualified_, T, Env, Size, Align ←
type_size_alignT, Env, Size, Align
2287size_align_bool, _, 1, 1
2288size_align_char, _, 1, 1
2289size_align_schar, _, 1, 1
2290size_align_uchar, _, 1, 1
2291size_align_short, _, 2, 2
2292size_align_ushort, _, 2, 2
2293size_align_int, _, 4, 4
2294size_align_uint, _, 4, 4
2295size_align_long, _, 8, 8
2296size_align_ulong, _, 8, 8
2297size_align_llong, _, 8, 8
2298size_align_ullong, _, 8, 8
2299size_align_float, _, 4, 4
2300size_align_double, _, 8, 8
2301size_align_ldouble, _, 16, 16
2302size_align_ptr_, _, 8, 8
2303size_align_atomic_, P, _, P, P
2304size_align_array_, N, _, _, _ ←
throwbad'variable length array'
2307size_align_arrayT, N, Env, Size, Align ←
integerN
type_size_alignT, Env, ElemSize, Align
Size is ElemSize × N
2311size_align_vecE, N, A, Env, Size, A ←
type_size_alignE, Env, ES, _
Size is ES × N
2313size_align_structId, Env, Size, Align ←
su_sizeId, Env, Size, Align
2314size_align_unionId, Env, Size, Align ←
su_sizeId, Env, Size, Align
2315size_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.

2319su_sizeId, e_, _, Defs, _, Size, Align ←
get_assocId, Defs, Def
def_size_alignDef, Size, Align
2323def_size_aligndef_, _, Size, Align, Size, Align
2324def_size_alignpartialWhy, _, _, _ ←
throwbadWhy
2326def_size_alignbadWhy, _, _ ←
throwbadWhy
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
2344sizeof_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.

2355complete_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.

2362member_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
2406member_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
2417named_membermsomeName, Type, Off, Name, Type, Off, none
2418named_membermsomeName, Type, Off, Bits, Name, Type, Off, Bits
2420known_membersdef_, Members, _, _, Members
2421known_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
2436int_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
2449bool1unsigned0
2450char1signed1
2451schar1signed1
2452uchar1unsigned1
2453short2signed2
2454ushort2unsigned2
2455int4signed3
2456uint4unsigned3
2457long8signed4
2458ulong8unsigned4
2459llong8signed5
2460ullong8unsigned5
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
2469int_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
2476rankType, 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
2486unsigned_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
2497scalar_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
2520arithmetic_typeT ←
2521arithmetic_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
2533default_promotionfloat, double ←
!
2534default_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
2548integer_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
2573usual_arith_conversionTypeA0, TypeB0, Type ←
TypeA0 ≡ ldouble
or
TypeB0 ≡ ldouble
!
Type = ldouble
2577usual_arith_conversionTypeA0, TypeB0, Type ←
TypeA0 ≡ double
or
TypeB0 ≡ double
!
Type = double
2581usual_arith_conversionTypeA0, TypeB0, float ←
TypeA0 ≡ float
or
TypeB0 ≡ float
!
2584usual_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
2617convert_intV, bool, W ←
!
V =:= 0
→
W = 0
else
W = 1
2618convert_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
2632signed_wordsigned, U, M, W ←
U ≥ M >> 1
→
W is U−M
else
W = U
2633signed_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
2648type_classT, K ←
integer_typeT, Size, _, _
Size =:= 8
→
K = l
else
K = w
2649type_classfloat, s
2650type_classdouble, d
2651type_classldouble, x
2652type_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.

2671const_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.

2683constant_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
2695float_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
2714float_builtin'__builtin_huge_val', double, 0x7ff0000000000000
2715float_builtin'__builtin_inf', double, 0x7ff0000000000000
2716float_builtin'__builtin_huge_valf', float, 0x7f800000
2717float_builtin'__builtin_inff', float, 0x7f800000
2718float_builtin'__builtin_huge_vall', ldouble, 0x7fff8000000000000000
2719float_builtin'__builtin_infl', ldouble, 0x7fff8000000000000000
2720float_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
2735'__builtin_nan'double0x7ff8000000000000
2736'__builtin_nanf'float0x7fc00000
2737'__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
2758const_convertV0, _, _, V ←
V0 ≡ ub
!
V = ub
2759const_convertV0, T0, T, V ←
T0 ≡ T
!
V = V0
2760const_convertV, From, bool, W ←
!
const_truthV, From, W
2761const_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
2776integer_rangesigned, Bits, Lo, Hi ←
Lo is −1 << Bits−1
Hi is −Lo−1
2777integer_rangeunsigned, Bits, 0, Hi ←
Hi is 1 << Bits−1
2779const_truthub, _, ub ←
!
2780const_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.

2789pp_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.

2798const_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
2834foldedval_, intV, _, V

6.4.4.1, 6.4.4.4

2835foldedvalT, floatCs, _, V ←

6.4.4.2

2836foldedval_, builtinName, [], _, V ←
float_builtinName, _, V
2837foldedval_, builtinName, Args, Env, V ←
object_sizeName, Args, Env, V
2838foldedval_, builtinName, A, Env, V ←
bit_foldedName, A, Env, V
2839foldedval_, unnot, XV, Env, V ←
6.5.3.3p5
operand_valueXV, Env, V0, T0
const_truthV0, T0, B
definedB, V, truthB =:= 0, V
2843foldedvalT0, unOp, XV, Env, V ←
6.5.3.3p2-4
Op ≢ not
operand_valueXV, Env, V0, _
definedV0, V, constant_unaryT, Op, V0, V
2848foldedvalT0, 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
2858foldedvalT0, 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
2864foldedvalT0, 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
2873foldedvalT0, cast_, XV, Env, V ←
6.5.4p5
operand_valueXV, Env, V0, From
const_convertV0, From, T, V
2877foldedvalT0, cast_, XV, Env, V ←
6.6p10
const_convertA, ulong, T, V
2881foldedval_, sizeoftypeSpecs, Decl, Env0, V ←
!6.5.3.4p2
type_name_typetypeSpecs, Decl, T, Env0, Env
catchsizeof_sizeT, Env, V, bad_, fail
2884foldedval_, sizeofX, Env, V ←
catchsizeof_sizeT, Env, V, bad_, fail
2887foldedval_, 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
2895foldedval_, offsetofTypeName, Path, Env, V ←
7.19p3
type_name_typeTypeName, T, Env, Env1
offset_pathPath, T, Env1, 0, V
2898foldedval_, commaXA, XV, Env, V ←
foldedXA, Env, _
foldedXV, Env, V
2899foldedval_, 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.

2905operand_valueXV, Env, V, T ←
foldedXV, Env, V
loweredXV, T
2907constant_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
2938constant_addressvalptr_, cast_, X, Env, A ←
X = valptr_, _
→
else
operand_valueX, Env, A, From
2942constant_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
2953constant_addressvalptr_, addrL, Env, A ←
2954constant_addressvalptr_, decayL, Env, A ←
2956lvalue_addressobj_, derefXP, Env, A ←
2957lvalue_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.

2964offset_path[], _, _, Off, Off
2965offset_pathfieldF|Path, T, Env, Off0, Off ←
member_type_offsetT, F, Env, FieldType, FieldOff
Off1 is Off0 + FieldOff
offset_pathPath, FieldType, Env, Off1, Off
2969offset_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.

2980env_int_typee_, _, _, _, T, T
2981env_int_typepp, T0, T ←
int_infoT0, _, Sign
2985intmax_typesigned, long
2986intmax_typeunsigned, ulong
defined+Values, -V, :Goal

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

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

V is the value V0 converted to T.

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

B is 1 if Goal holds, else 0.

2997truthGoal, 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
3001negX−X
3002plusXX
3003bnotX\X
binary_kind?Op, ?Kind

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

3007binary_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
3049kind_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
3060kind_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
3068kind_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
3078kind_binarylogical, Op, VA−TA, VB−TB, _, V ←
const_truthVA, TA, A
const_truthVB, TB, B
definedA, V, const_logicalOp, A, B, V
3082const_shiftshl, A, B, R ←
R is A << B
3083const_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.

3087const_logicalland, A, VB, V ←
A =:= 0
→
V = 0
else
definedVB, V, truthVB ≠ 0, V
3089const_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.

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

Division by zero is undefined: R is ub.

3101const_arithadd, A, B, R ←
R is A + B
3102const_arithsub, A, B, R ←
R is A−B
3103const_arithmul, A, B, R ←
R is A × B
3104const_arithdiv, A, B, R ←
B =:= 0
→
R = ub
else
R is A//B
3105const_arithmod, A, B, R ←
B =:= 0
→
R = ub
else
R is A rem B
3106const_arithand, A, B, R ←
R is A /\ B
3107const_arithor, A, B, R ←
R is A \/ B
3108const_arithxor, A, B, R ←
R is A xor B
3110const_compareeq, A, B ←
A =:= B
3111const_comparene, A, B ←
A ≠ B
3112const_comparelt, A, B ←
A < B
3113const_comparegt, A, B ←
A > B
3114const_comparele, A, B ←
A ≤ B
3115const_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.

3122num_typeV, Suffix−Radix, Type ←
constant_typesRadix, Suffix, Types
memberType, Types
fitsV, Type
→
true
else
Type = ullong
constant_types
3130decnoneint, long, llong
3131decuuint, ulong, ullong
3132decllong, llong
3133decululong, ullong
3134decllllong
3135decullullong
3136othernoneint, uint, long, ulong, llong, ullong
3137otheruuint, ulong, ullong
3138otherllong, ulong, llong, ullong
3139otherululong, ullong
3140otherllllong, ullong
3141otherullullong
fits+V, +Type

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

3144fitsV, 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.

3151below_maxsigned, V, Bits ←
V < 1 << Bits−1
3152below_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
3169compatible_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).

3173compatible_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.

examples 10/10 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_typearrayint, 2, arrayint, 3, _⟹fails
✓composite_typefuncint, [], fixed, funcint, [], variadic, _⟹fails
3210composite_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
3225composite_typeT, U, T, _ ←
T ≡ U
!
3226composite_typeT, U, C, Env ←
enum_markedT, K, T1
!
enum_markedU, L, U1
→
K ≡ L
else
U1 = U
composite_typeT1, U1, C, Env
3229composite_typeT, U, C, Env ←
enum_markedU, _, U1
!
composite_typeT, U1, C, Env
3230composite_typequalifiedQs, T, qualifiedQs, U, qualifiedQs, C, Env ←
composite_typeT, U, C, Env
3232composite_typeptrT, ptrU, ptrC, Env ←
composite_typeT, U, C, Env
3233composite_typearrayT, N, arrayU, M, arrayC, K, Env ←
composite_typeT, U, C, Env
3236composite_typefuncR, Ps, V, funcS, Qs, V, funcC, Cs, V, Env ←
composite_typeR, S, C, Env
composite_paramsPs, Qs, Cs, Env
enum_marked+T, -K, -T1

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

3242enum_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.

3247composite_sizenone, M, M ←
!
3248composite_sizeN, none, N ←
!
3249composite_sizeN, M, M ←
3250composite_sizeN, M, N ←
3251composite_sizeN, N, N
composite_params+Ps, +Qs, -Cs

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

3255composite_paramsunspecified, Qs, Qs, _ ←
!
3256composite_paramsPs, unspecified, Ps, _ ←
!
3257composite_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).

3264composite_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
3286unqualifiedT, T ←
varT
!
3287unqualifiedqualified_, T0, T ←
3288unqualifiedptrT0, ptrT ←
3289unqualifiedarrayT0, N, arrayT, N ←
3290unqualifiedfuncR0, Ps0, V, funcR, Ps, V ←
!
Ps0 ≡ unspecified
→
Ps = Ps0
else
maplistunqualified, Ps0, Ps
3293unqualifiedT, 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
3358elaborateE, Env, X ←
elabE, Env, X0
!
X = X0
3360elabidName, Env, X ←
lookup_ordinaryEnv, Name, Binding
!
identifier_typeBinding, Name, Env, T
binding_nodeBinding, T, Name, X
3364elabidName, 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
3372elabnumV0, S, Env, valT, intV ←
num_typeV0, S, T0
env_int_typeEnv, T0, T
convert_intV0, T, V
3374elabchrV, Env, valT, intV ←
env_int_typeEnv, int, T
3375elabfloatCs, Env, valT, floatCs ←
lastCs, Suffix
memberchkSuffix, f, 'F'
→
T0 = float
memberchkSuffix, l, 'L'
→
T0 = ldouble
else
T0 = double
env_int_typeEnv, T0, T
3382elabstrBytes, _, objarraychar, N, strBytes ←
lengthBytes, M
N is M + 1
3384elabwstrT, Values, _, objarrayT, N, wstrT, Values ←
lengthValues, M
N is M + 1
3386elabcastTN, 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
3391elabcastTN, E, Env, valT, castTN, XV ←
type_name_typeTN, T0, Env, _
operandE, Env, XV, F0
reflect_typeTN, T0, Env, T
3396elabcomplitTN, Init, Env, objT, complitTN, Init ←
type_name_typeTN, T0, Env, _
reflect_typeTN, T0, Env, T
3398elabunaddr, E, Env, valptrT, addrX ←
6.5.3.2p1
elaborateE, Env, X
X ≠ val_, _
X ≠ obj_, bitfield_, _
3401elabunderef, E, Env, X ←
operandE, Env, XV, ptrT
designatorT, derefXV, X
3404elabunOp, E, Env, valT, incdecOp, X ←
6.5.3.1p1
memberchkOp, preinc, predec, postinc, postdec
elaborateE, Env, X
valueX, XV
3408elabunOp, E, Env, valT, unOp, XV ←
memberchkOp, plus, neg, bnot, not
operandE, Env, XV, T1
unary_operandOp, T1, T0
env_int_typeEnv, T0, T
3412elabindexA, 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
3420elabindexA, I, Env, X ←
6.5.2.1p2
elabunderef, binadd, A, I, Env, X
3422elabmemberE, 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
3429elabarrowE, 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.

3433elabcallidzhas_union, E, _, valvoid, builtinzhas_union, E ←
!
3434elabcallid'__builtin_constant_p', E, Env, X ←
¬lookup_ordinaryEnv, '__builtin_constant_p', _
!
elabnumV, none−dec, Env, X
3438elabcallidName, 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
3445elabcallidName, Args, Env, X ←
¬lookup_ordinaryEnv, Name, _
variable_ordersName, Args, Env, Evaluated, Args1
!
foldlevaluated_first, Evaluated, callidName, Args1, E
elabE, Env, X
3450elabcallidName, Args, Env, X ←
¬lookup_ordinaryEnv, Name, _
atomic_builtinName, Args, Env, R
!
R = exprE
→
elabE, Env, X
else
R = builtinT
X = valT, builtinName, Args
3456elabcallidName, str[], Env, valT, builtinName, [] ←
¬lookup_ordinaryEnv, Name, _
nan_builtinName, T, _
!
3459elabcallidName, Args, Env, valT, builtinName, Args ←
GNU vectors
¬lookup_ordinaryEnv, Name, _
!
vector_builtin_typeName, Args, Env, T
3463elabcallidName, Args, Env, valT, builtinName, Args ←
¬lookup_ordinaryEnv, Name, _
Args ≡ []
float_builtinName, T, _
¬nan_builtinName, _, _
→
true
else
3468elabcallE, 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
3473elabassignOp, A, B, Env, valT, assignOp, X, XB ←
6.5.16p2
elaborateA, Env, X
argumentEnv, B, XB
loweredXB, TB
assignsOp, TA, XB, TB, Env
3478elabcommaA, B, Env, valT, commaXA, XV ←
argumentEnv, A, XA
operandB, Env, XV, T
3481elabsizeof_exprE, Env, valulong, sizeofX ←
6.5.3.4p1
elaborateE, Env, X
X ≠ obj_, bitfield_, _
3483elabsizeof_typeTN, _, valulong, sizeofTN
3484elabalignofTN, _, valulong, alignofTN
3485elaboffsetofTN, Path, _, valulong, offsetofTN, Path
3486elablabel_addrName, Env, X ←
GNU &&label
label_valueName, Env, E
elabE, Env, X
3488elabbinOp, A, B, Env, valT, binOp, XA, XB ←
operandA, Env, XA, TA
operandB, Env, XB, TB
env_int_typeEnv, T0, T
3491elabcondC, A, B, Env, valT, condXC, XA, XB ←
6.5.15p2
argumentEnv, C, XC
tree_typeXC, TC
armsA, B, Env, XA, XB, T
3494elabgcondC, B, Env, valT, gcondXC, XB ←
armsC, B, Env, XC, XB, T
tree_typeXC, TC
3496elabva_argA, TN, Env, valT, va_argTN, XA ←
argumentEnv, A, XA
type_name_typeTN, T0, Env, _
reflect_typeTN, T0, Env, T
3499elabbit_castTN, E, Env, valT, bit_castXA ←
argumentEnv, E, XA
tree_typeXA, TA
type_name_typeTN, T0, Env, _
bit_castableTA, T, Env
3503elabstmtexpratLoc, blockItems, Env, valT, stmtexpratLoc, blockItems ←
block_value_typeItems, Env, T
3505elabtypes_compatibleTA, TB, Env, X ←
!
elabnumV, none−dec, Env, X
3508elabcallidName, 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
3516elabcallid'__builtin_classify_type', E, Env, X ←
¬lookup_ordinaryEnv, '__builtin_classify_type', _
expression_typeE, Env, T0
!
elabnumV, none−dec, Env, X
3521elabcallid'__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'
3527elabcallidName, Args, Env, X ←
¬lookup_ordinaryEnv, Name, _
hint_builtinName, Args, E
!
elabcasttypetspecvoid, base, E, Env, X
3531elabgenericC, 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.

3543argumentEnv, 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).

3547assignment_typeobjT0, bitfield_, W, T ←
3549assignment_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
3560loweredXV, 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).

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

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

3572arity_fitsunspecified, _, _ ←
!
3573arity_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.

3579passesunspecified, _, _ ←
!
3580passes[], _, _
3581passesP|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
3602identifier_typeenumconst_, T0, _, _, T ←
!
T = T0
3603identifier_typeBinding, Name, Env, T ←
binding_typeBinding, T0
lookup_ordinaryEnv, qtypeName, RT
→
else
T = T0
binding_type
3609var_, TT
3610gvarT, _, _, _T
3611fnT, _, _T
3612enumconst_, 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
3634readonly_object_typequalifiedQs, T ←
memberchkconst, Qs
→
true
3636readonly_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.

3641reflected_shapequalifiedQs, R, T, qualifiedQs, S ←
3643reflected_shapearrayR, _, arrayT, N, arrayS, N ←
3645reflected_shapeptrR, ptrT, ptrS ←
vm_typeTevaluated lengths
!
3647reflected_shapeR, _, R
binding_node+Binding, +T, +Name, -X

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

binding_node
3651var_, _TNameobjT, idName
3652gvar_, _, _, _TNameobjT, idName
3653fn_, _, _TNamefunT, idName
3654enumconstV, _T_valT, intV
designator+T, +F, -X

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

3658designatorT, F, X ←
T = func_, _, _
!
X = funT, F
3659designatorT, 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).

3663member_nodeobjTS, FS, F, T, objT, memberobjTS, FS, F
3664member_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
3682member_typequalifiedQs, SU, F, Env, T ←
!
member_typeSU, F, Env, T0
qualifyQs, T0, T
3684member_typeSU, F, Env, T ←
SU =.. _, Id
Env = e_, _, Defs, _
get_assocqmembersId, Defs, Members
!
members_typeMembers, F, Env, T
3688member_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.

3693members_typeMembers, F, _, T ←
memberchkqsomeF, T0, Members
!
T = T0
3695members_typeMembers, F, Env, T ←
memberqnone, Anon, Members
member_typeAnon, F, Env, T
!
library_builtin?Name, ?Fnondet
Namein/outa GNU builtin spelling of a standard 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). 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.4
examples 2/2 hold
✓library_builtin'__builtin_memcpy', F⟹F = memcpy
✓library_builtinmemcpy, _⟹fails
3711library_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
3719memcpyvpvp, cvp, size
3720memmovevpvp, cvp, size
3721memsetvpvp, int, size
3722memcmpintcvp, cvp, size
3723memchrvpcvp, int, size
3724strlensizeccp
3725strcmpintccp, ccp
3726strncmpintccp, ccp, size
3727strcpycpcp, ccp
3728strncpycpcp, ccp, size
3729strcatcpcp, ccp
3730strncatcpcp, ccp, size
3731strchrcpccp, int
3732strrchrcpccp, int
3733strstrcpccp, ccp
3734strdupcpccp
3735mallocvpsize
3736callocvpsize, size
3737reallocvpvp, size
3738freevoidvp
3739abortvoid[]
3740absintint
3741labslonglong
proto_type
3743vptspecvoidptrbase, []
3744cvpqualconst, tspecvoidptrbase, []
3745cptspeccharptrbase, []
3746ccpqualconst, tspeccharptrbase, []
3747inttspecintbase
3748longtspeclongbase
3749sizetspecunsigned, tspeclongbase
3750voidtspecvoidbase
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
3759builtin_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
3789label_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
3807checking_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.

3815unchecked_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
3837object_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
3971atomic_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.

3978atomic_builtin'__c11_atomic_load', P, O, Env, exprcommaO, underef, P ←
order_inO, Env, 0, 1, 2, 5
!
3980atomic_builtin'__c11_atomic_store', P, V, O, Env, exprcasttypetspecvoid, base, commaO, assign=, underef, P, V ←
order_inO, Env, 0, 3, 5
!
3983atomic_builtin'__c11_atomic_init', P, V, Env, exprcasttypetspecvoid, base, assign=, underef, P, V ←
3986atomic_builtin'__c11_atomic_exchange', P, _, O, Env, builtinS ←
3988atomic_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
!
3996atomic_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
4001atomic_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
4006atomic_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
4012atomic_builtin'__c11_atomic_store', P, _, O, Env, builtinvoid ←
c11_operandsP, O, Env, 0, 3, 5, _
4015atomic_builtin'__c11_atomic_init', P, _, Env, builtinvoid ←
4018atomic_builtinName, O, Env, builtinvoid ←
catchconst_valueO, Env, V, _, not_constant, fail
between0, 5, V
4023atomic_builtinName, P, _, O, Env, builtinT ←
rmw_builtinName, c11, Op, _
lowered_typeP, Env, ptrT0
4028atomic_builtinName, P, E, _, Os, Of, Env, builtinbool ←
memberchkName, '__c11_atomic_compare_exchange_strong', '__c11_atomic_compare_exchange_weak'
cas_operandsP, E, Env
4034atomic_builtinName, P, _, O, Env, builtinT ←
rmw_builtinName, gnu, Op, _
lowered_typeP, Env, ptrT0
4038atomic_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
4043atomic_builtinName, P, _|_, Env, builtinT ←
rmw_builtinName, sync, Op, _
lowered_typeP, Env, ptrT0
4046atomic_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
4051atomic_builtin'__sync_lock_release', P|_, Env, builtinvoid ←
lowered_typeP, Env, ptrT0
4053atomic_builtin'__sync_synchronize', [], _, builtinvoid
4054atomic_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.

4061generic_atomic'__atomic_load', P, R, O, casttypetspecvoid, base, assign=, underef, R, callid'__atomic_load_n', P, O
4064generic_atomic'__atomic_store', P, V, O, callid'__atomic_store_n', P, underef, V, O
4066generic_atomic'__atomic_exchange', P, V, R, O, casttypetspecvoid, base, assign=, underef, R, callid'__atomic_exchange_n', P, underef, V, O
4070generic_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
4082rmw_builtin'__c11_atomic_exchange', c11, xchg, old
4083rmw_builtinName, c11, Op, old ←
rmw_opOp, _
Op ≢ xchg
atom_concat'__c11_atomic_fetch_', Op, Name
4085rmw_builtin'__atomic_exchange_n', gnu, xchg, old
4086rmw_builtinName, gnu, Op, old ←
rmw_opOp, _
Op ≢ xchg
atom_concat'__atomic_fetch_', Op, Name
4088rmw_builtinName, gnu, Op, new ←
rmw_opOp, _
Op ≢ xchg
atomic_list_concat'__atomic_', Op, '_fetch', Name
4090rmw_builtin'__sync_lock_test_and_set', sync, xchg, old
4091rmw_builtinName, sync, Op, old ←
rmw_opOp, sync
atom_concat'__sync_fetch_and_', Op, Name
4093rmw_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
4098xchgnone
4099addsync
4099subsync
4099andsync
4100orsync
4100xorsync
4100nandsync
4101maxnone
4101minnone
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.

4106rmw_operand_, T ←
4107rmw_operandOp, ptr_ ←
memberchkOp, xchg, add, sub
4108rmw_operandxchg, T ←
memberchkT, float, double
gnu_operand+Op, +T

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

4112gnu_operand_, T ←
4113gnu_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
4126cas_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
4145variable_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.

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

the argument positions of builtin Name's memory orders.

4163order_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
4174order_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.

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

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

4189atomic_su_pointeeP, Env, S ←
lowered_typeP, Env, ptrT0
unqualifiedT0, atomicS, _
4192order_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.

4198c11_operandsP, O, Env, Orders, T ←
lowered_typeP, Env, ptrT0
catchconst_valueO, Env, V, _, not_constant, fail
memberchkV, Orders
4204atomic_name'__atomic_load_n'
4205atomic_name'__atomic_store_n'
4206atomic_name'__c11_atomic_load'
4207atomic_name'__c11_atomic_store'
4208atomic_name'__c11_atomic_init'
4209atomic_nameName ←
4210atomic_nameName ←
rmw_atomic?Name

an atomic read-modify-write builtin.

4213rmw_atomicName ←
rmw_builtinName, _, _, _
4214rmw_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).

4225atomic_operandsP, O, Env, Orders, T ←
atomic_operandsP, O, Env, Orders, T, _
4226atomic_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
4238rotate_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 9/9 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_assume_aligned', T⟹T = ptrvoid
✓builtin_type'__builtin_ctzlll', _⟹fails
4273builtin_type'__builtin_va_start', void
4274builtin_type'__builtin_va_copy', void
4275builtin_type'__builtin_va_end', void
4276builtin_type'__builtin_unreachable', void
4277builtin_type'__builtin_alloca', ptrvoid
4278builtin_type'__builtin_frame_address', ptrvoid
4279builtin_type'__builtin_expect', long
4280builtin_type'__builtin_assume_aligned', ptrvoid
4281builtin_type'__builtin_constant_p', int
4282builtin_type'__builtin_cpu_init', void
4283builtin_type'__builtin_cpu_supports', int
4284builtin_type'__builtin_cpu_is', int
4285builtin_typeName, bool ←
4286builtin_typeName, int ←
bit_builtinName, _, _
4287builtin_type'__builtin_bswap16', ushort
4288builtin_type'__builtin_bswap32', uint
4289builtin_type'__builtin_bswap64', ulong
4290builtin_typeName, Type ←
rotate_builtinName, _, Type
4291builtin_typeName, double ←
4292builtin_type'__builtin_isnan', int
4293builtin_type'__builtin_flt_rounds', int
4294builtin_type'__builtin_object_size', ulong
4295builtin_type'__builtin_dynamic_object_size', ulong
4296builtin_typeName, int ←
4297builtin_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
4315cmov'__cpu_model'120x1
4316mmx'__cpu_model'120x2
4317popcnt'__cpu_model'120x4
4318sse'__cpu_model'120x8
4319sse2'__cpu_model'120x10
4320sse3'__cpu_model'120x20
4321ssse3'__cpu_model'120x40
4322'sse4.1''__cpu_model'120x80
4323'sse4.2''__cpu_model'120x100
4324avx'__cpu_model'120x200
4325avx2'__cpu_model'120x400
4326sse4a'__cpu_model'120x800
4327fma4'__cpu_model'120x1000
4328xop'__cpu_model'120x2000
4329fma'__cpu_model'120x4000
4330avx512f'__cpu_model'120x8000
4331bmi'__cpu_model'120x10000
4332bmi2'__cpu_model'120x20000
4333aes'__cpu_model'120x40000
4334pclmul'__cpu_model'120x80000
4335avx512vl'__cpu_model'120x100000
4336avx512bw'__cpu_model'120x200000
4337avx512dq'__cpu_model'120x400000
4338avx512cd'__cpu_model'120x800000
4339avx512vbmi'__cpu_model'120x4000000
4340avx512ifma'__cpu_model'120x8000000
4341avx5124vnniw'__cpu_model'120x10000000
4342avx5124fmaps'__cpu_model'120x20000000
4343avx512vpopcntdq'__cpu_model'120x40000000
4344avx512vbmi2'__cpu_model'120x80000000
4345gfni'__cpu_features2'00x1
4346vpclmulqdq'__cpu_features2'00x2
4347avx512vnni'__cpu_features2'00x4
4348avx512bitalg'__cpu_features2'00x8
4349avx512bf16'__cpu_features2'00x10
4350avx512vp2intersect'__cpu_features2'00x20
4351adx'__cpu_features2'00x100
4352cldemote'__cpu_features2'00x400
4353clflushopt'__cpu_features2'00x800
4354clwb'__cpu_features2'00x1000
4355clzero'__cpu_features2'00x2000
4356enqcmd'__cpu_features2'00x10000
4357f16c'__cpu_features2'00x20000
4358fsgsbase'__cpu_features2'00x40000
4359lwp'__cpu_features2'00x1000000
4360lzcnt'__cpu_features2'00x2000000
4361movbe'__cpu_features2'00x4000000
4362movdir64b'__cpu_features2'00x8000000
4363movdiri'__cpu_features2'00x10000000
4364mwaitx'__cpu_features2'00x20000000
4365pconfig'__cpu_features2'00x80000000
4366pku'__cpu_features2'40x1
4367prfchw'__cpu_features2'40x4
4368ptwrite'__cpu_features2'40x8
4369rdpid'__cpu_features2'40x10
4370rdrnd'__cpu_features2'40x20
4371rdseed'__cpu_features2'40x40
4372rtm'__cpu_features2'40x80
4373serialize'__cpu_features2'40x100
4374sgx'__cpu_features2'40x200
4375sha'__cpu_features2'40x400
4376shstk'__cpu_features2'40x800
4377tbm'__cpu_features2'40x1000
4378tsxldtrk'__cpu_features2'40x2000
4379vaes'__cpu_features2'40x4000
4380waitpkg'__cpu_features2'40x8000
4381wbnoinvd'__cpu_features2'40x10000
4382xsave'__cpu_features2'40x20000
4383xsavec'__cpu_features2'40x40000
4384xsaveopt'__cpu_features2'40x80000
4385xsaves'__cpu_features2'40x100000
4386'amx-tile''__cpu_features2'40x200000
4387'amx-int8''__cpu_features2'40x400000
4388'amx-bf16''__cpu_features2'40x800000
4389uintr'__cpu_features2'40x1000000
4390hreset'__cpu_features2'40x2000000
4391kl'__cpu_features2'40x4000000
4392widekl'__cpu_features2'40x10000000
4393avxvnni'__cpu_features2'40x20000000
4394avx512fp16'__cpu_features2'40x40000000
4395'x86-64''__cpu_features2'40x80000000
4396'x86-64-v2''__cpu_features2'80x1
4397'x86-64-v3''__cpu_features2'80x2
4398'x86-64-v4''__cpu_features2'80x4
4399avxifma'__cpu_features2'80x8
4400avxvnniint8'__cpu_features2'80x10
4401avxneconvert'__cpu_features2'80x20
4402cmpccxadd'__cpu_features2'80x40
4403'amx-fp16''__cpu_features2'80x80
4404prefetchi'__cpu_features2'80x100
4405raoint'__cpu_features2'80x200
4406'amx-complex''__cpu_features2'80x400
4407avxvnniint16'__cpu_features2'80x800
4408sm3'__cpu_features2'80x1000
4409sha512'__cpu_features2'80x2000
4410sm4'__cpu_features2'80x4000
4411apxf'__cpu_features2'80x8000
4412usermsr'__cpu_features2'80x10000
4413'avx10.1-256''__cpu_features2'80x20000
4414'avx10.1-512''__cpu_features2'80x40000
4415'avx10.2-256''__cpu_features2'80x80000
4416'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
4430intel01
4431amd02
4432atom41
4433core242
4434corei743
4435amdfam10h44
4436amdfam15h45
4437amdfam17h410
4438amdfam19h415
4439amdfam1ah420
4440bonnell41
4441silvermont46
4442slm46
4443knl47
4444knm411
4445goldmont412
4446'goldmont-plus'413
4447tremont414
4448sierraforest417
4449grandridge418
4450clearwaterforest419
4451nehalem81
4452westmere82
4453sandybridge83
4454ivybridge812
4455haswell813
4456broadwell814
4457skylake815
4458'skylake-avx512'816
4459cannonlake817
4460'icelake-client'818
4461'icelake-server'819
4462cascadelake821
4463tigerlake822
4464cooperlake823
4465sapphirerapids824
4466alderlake825
4467raptorlake825
4468meteorlake825
4469rocketlake827
4470graniterapids830
4471'graniterapids-d'831
4472arrowlake832
4473'arrowlake-s'833
4474lunarlake833
4475pantherlake834
4476gracemont825
4477emeraldrapids824
4478diamondrapids836
4479barcelona84
4480shanghai85
4481istanbul86
4482btver148
4483btver249
4484bdver187
4485bdver288
4486bdver389
4487bdver4810
4488znver1811
4489znver2820
4490znver3826
4491znver4829
4492znver5835
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. __builtin_sqrt(x) is sqrt(x), correctly rounded either way; for x < 0 libm's sets errno to EDOM, as clang's call under the default -fmath-errno does (cogl's cogl_sqrti).

GCC manual, Other Built-in FunctionsC11 7.12.9.1p2C11 7.12.9.2p2C11 7.12.7.5
examples 3/3 hold
✓libm_builtin'__builtin_floor', F⟹F = floor
✓libm_builtin'__builtin_sqrt', F⟹F = sqrt
✓libm_builtin'__builtin_round', _⟹fails
libm_builtin
4540'__builtin_ceil'ceil
4541'__builtin_floor'floor
4542'__builtin_sqrt'sqrt
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
4557vector_builtinName, Params, Ret ←
vector_builtin_Short, Params0, Ret0
atom_concat'__builtin_ia32_', Short, Name
maplistvector_abbrev, Params0, Params
vector_abbrevRet0, Ret
4563vector_abbrevv16qi, vecchar, 16, 16 ←
!
4564vector_abbrevv8hi, vecshort, 8, 16 ←
!
4565vector_abbrevv4si, vecint, 4, 16 ←
!
4566vector_abbrevv2di, vecllong, 2, 16 ←
!
4567vector_abbrevv4sf, vecfloat, 4, 16 ←
!
4568vector_abbrevv2df, vecdouble, 2, 16 ←
!
4569vector_abbrevv32qi, vecchar, 32, 32 ←
!
4570vector_abbrevv16hi, vecshort, 16, 32 ←
!
4571vector_abbrevv8si, vecint, 8, 32 ←
!
4572vector_abbrevv4di, vecllong, 4, 32 ←
!
4573vector_abbrevv8sf, vecfloat, 8, 32 ←
!
4574vector_abbrevv4df, vecdouble, 4, 32 ←
!
4575vector_abbrevT, T
4577vector_builtin_pmovmskb128, v16qi, int
4578vector_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
4586vector_builtin_pmovmskb256, v32qi, int
4587vector_builtin_addsubps, v4sf, v4sf, v4sf
4588vector_builtin_addsubpd, v2df, v2df, v2df
4589vector_builtin_Name, V, V, V, V ←
memberV−Ws, v4sf−vfmaddps, vfmaddsubps, v2df−vfmaddpd, vfmaddsubpd
memberName, Ws
4592vector_builtin_vpermilps, v4sf, int, v4sf
4593vector_builtin_vpermilpd, v2df, int, v2df
4594vector_builtin_vpermilpd256, v4df, int, v4df
4595vector_builtin_shufpd256, v4df, v4df, int, v4df
4596vector_builtin_movmskps256, v8sf, int
4597vector_builtin_movmskpd256, v4df, int
4598vector_builtin_Name, W, int, H ←
memberName−W−H, extract128i256−v4di−v2di, vextractf128_si256−v8si−v4si, vextractf128_ps256−v8sf−v4sf, vextractf128_pd256−v4df−v2df
4601vector_builtin_Name, W, H, int, W ←
memberName−W−H, insert128i256−v4di−v2di, vinsertf128_si256−v8si−v4si, vinsertf128_ps256−v8sf−v4sf, vinsertf128_pd256−v4df−v2df
4604vector_builtin_Name, W, W, int, W ←
memberName−W, permti256−v4di, vperm2f128_si256−v8si, vperm2f128_ps256−v8sf, vperm2f128_pd256−v4df
4607vector_builtin_permvarsi256, v8si, v8si, v8si
4608vector_builtin_permvarsf256, v8sf, v8si, v8sf
4609vector_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
4612vector_builtin_vzeroupper, [], void
4613vector_builtin_vzeroall, [], void
4614vector_builtin_lddqu, ptrqualifiedconst, char, v16qi
4615vector_builtin_movmskps, v4sf, int
4616vector_builtin_movmskpd, v2df, int
4617vector_builtin_Name, V, int, V ←
memberV−Ws, v8hi−psllwi128, psrlwi128, psrawi128, v4si−pslldi128, psrldi128, psradi128, v2di−psllqi128, psrlqi128, pslldqi128_byteshift, psrldqi128_byteshift
memberName, Ws
4622vector_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
4629vector_builtin_packsswb128, v8hi, v8hi, v16qi
4630vector_builtin_packuswb128, v8hi, v8hi, v16qi
4631vector_builtin_packssdw128, v4si, v4si, v8hi
4632vector_builtin_packusdw128, v4si, v4si, v8hi
4633vector_builtin_pshufd, v4si, int, v4si
4634vector_builtin_pshuflw, v8hi, int, v8hi
4635vector_builtin_pshufhw, v8hi, int, v8hi
4636vector_builtin_palignr128, v16qi, v16qi, int, v16qi
4637vector_builtin_pblendvb128, v16qi, v16qi, v16qi, v16qi
4638vector_builtin_pblendw128, v8hi, v8hi, int, v8hi
4639vector_builtin_pmuludq128, v4si, v4si, v2di
4640vector_builtin_pmuldq128, v4si, v4si, v2di
4641vector_builtin_pclmulqdq128, v2di, v2di, char, v2di
4642vector_builtin_shufpd, v2df, v2df, int, v2df
4643vector_builtin_shufps, v4sf, v4sf, int, v4sf
4644vector_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
4648vector_builtin_pmaddwd128, v8hi, v8hi, v4si
4649vector_builtin_pmaddubsw128, v16qi, v16qi, v8hi
4650vector_builtin_psadbw128, v16qi, v16qi, v2di
4651vector_builtin_ptestz128, v2di, v2di, int
4652vector_builtin_ptestc128, v2di, v2di, int
4653vector_builtin_ptestnzc128, v2di, v2di, int
4654vector_builtin_vec_ext_v16qi, v16qi, int, char
4655vector_builtin_vec_ext_v8hi, v8hi, int, short
4656vector_builtin_vec_ext_v4si, v4si, int, int
4657vector_builtin_vec_ext_v2di, v2di, int, llong
4658vector_builtin_vec_ext_v4sf, v4sf, int, float
4659vector_builtin_vec_set_v16qi, v16qi, char, int, v16qi
4660vector_builtin_vec_set_v8hi, v8hi, short, int, v8hi
4661vector_builtin_vec_set_v4si, v4si, int, int, v4si
4662vector_builtin_vec_set_v2di, v2di, llong, int, v2di
4663vector_builtin_undef128, [], v2df
4664vector_builtin_cvtss2si, v4sf, int
4665vector_builtin_cvtss2si64, v4sf, llong
4666vector_builtin_cvtsd2si, v2df, int
4667vector_builtin_cvtsd2si64, v2df, llong
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
4681lane_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
4702lane_builtinpshufd256, pshufd
4703lane_builtinpshuflw256, pshuflw
4704lane_builtinpshufhw256, pshufhw
4706shift_count_builtinName ←
memberchkName, psllw128, pslld128, psllq128, psrlw128, psrld128, psrlq128, psraw128, psrad128
4710wide_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
4728generic_vector_builtin'__builtin_shufflevector'
4729generic_vector_builtin'__builtin_convertvector'
4730generic_vector_builtin'__builtin_nondeterministic_value'
4731generic_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.

4742vector_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
4745vector_builtin_type'__builtin_convertvector', A, TN, Env, T ←
!
operandA, Env, _, vec_, N, _
type_name_typeTN, T0, Env, _
T = vec_, N, _
4748vector_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
4762bit_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
4783bit_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
4791bit_count_valuepopcount, _, X, V ←
V is popcountX
4792bit_count_valueffs, _, X, V ←
X =:= 0
→
V = 0
else
V is lsbX + 1
4793bit_count_valuectz, _, X, V ←
X > 0
V is lsbX
4794bit_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
4804overflow_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.

4817block_value_type[], _, void
4818block_value_typeItem, Env, T ←
!
last_item_typeItem, Env, T
4819block_value_typedecl_, Specs, Inits|Items, Env0, T ←
!
decl_typesSpecs, Inits, Decls, Env0, Env1
foldlbind_decl, Decls, Env1, Env
block_value_typeItems, Env, T
4823block_value_type_|Items, Env, T ←
block_value_typeItems, Env, T
4825bind_decldName, Type, _, _, _, Env0, Env ←
bind_ordinaryName, varnone, Type, Env0, Env
4828last_item_typeat_, exprE, Env, T ←
!
operandE, Env, _, T
4829last_item_typeat_, label_, Body, Env, T ←
!
last_item_typeBody, Env, T
4830last_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
4839tree_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
4848obj_, _lvalue
4849fun_, _function
4850val_, _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
4868valueobjT, bitfieldM, W, valV, loadobjT, bitfieldM, W ←
4870valueobjT, F, valV, decayobjT, F ←
4871valueobjT, F, valV, loadobjT, F ←
4872valuefunT, F, valptrT, decayfunT, F
4873valuevalT, 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
4892bitfield_promotionT, W, P ←
int_infoT, _, Sign
Sign ≡ signed
→
W ≤ 32
else
W < 32
→
P = int
W ≤ 32
→
P = uint
else
P = T
4899array_typequalified_, T ←
4900array_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
4911value_typequalified_, T, R ←
4912value_typeatomicS, _, S ←
!
4913value_typearrayT, _, ptrT ←
!
4914value_typefuncR, P, V, ptrfuncR, P, V ←
!
4915value_typeT, T
operand+E, +Env, -XV, -T

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

4919operandE, 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
4931expression_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
4948expression_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
4966modifiable_lvalueE, Env ←
elaborateE, Env, X
modifiable+X, +Env

typed tree X designates a modifiable object.

4969modifiableobjT, _, Env ←
4971modifiable_typequalifiedQs, T, Env ←
!
¬memberchkconst, Qs
4973modifiable_typearray_, _, _ ←
!
fail
4974modifiable_typevoid, _ ←
!
fail
4975modifiable_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.

4979const_withinqualifiedQs, _, _ ←
memberchkconst, Qs
!
4980const_withinqualified_, T, Env ←
4981const_withinarrayT, _, Env ←
4982const_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
5003unary_operandOp, T0, T0 ←
GNU vectors
T0 = vecE, _, _
!
memberchkOp, plus, neg
→
true
else
Op ≡ bnot
5006unary_operandOp, T0, T ←
memberchkOp, plus, neg
5008unary_operandbnot, T0, T ←
5009unary_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).

5013expression_binary_typeOp, A, B, int ←
6.5.13p2, 6.5.14p2
memberchkOp, land, lor
!
5016expression_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
5041vector_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
5056vector_scalarE, S ←
5057vector_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
5092binary_operandsOp, TA, TB, C, C, T ←
TA = vec_, _, _
or
TB = vec_, _, _
!
vector_operandsOp, TA, TB, C, T
5095binary_operandsOp, TA, TB, CA, CB, CA ←
memberchkOp, shl, shr
!
5098binary_operandsOp, TA, TB, C, C, T ←
callDomain, TA
callDomain, TB
!
→
T = int
else
T = C
5103binary_operandsOp, ptrP, TB, ptrP, long, ptrP ←
memberchkOp, add, sub
!
5105binary_operandsadd, TA, ptrP, long, ptrP, ptrP ←
5106binary_operandssub, ptrP, ptr_, ptrP, ptrP, long ←
!
5107binary_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.

5112arithmetic_operatorOp, Domain ←
c_operator_, _, Op, Kind
operand_domainKind, Domain
operand_domain
5116arithintegerint_type
5117aritharithmeticarithmetic_type
5118comparearithmetic_type
5120comparisonOp ←
c_operator_, _, Op, compare
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
5142castable_, void ←
!
5144castableptr_, B ←
B = ptr_
or
!
5145castableA, ptr_ ←
5146castableT, U ←
T ≡ U
T = struct_
or
T = union_
5147castablevecE, N, _, vecF, M, _ ←
GNU vectors
5150vector_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
5179assignableFrom, Null, To ←
assignableFrom, Null, To, none
assignable+From, +Null, +To, +Env

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

5183assignableFrom, _, To, _ ←
5184assignableT, _, U, _ ←
T ≡ U
!
5185assignableS, _, atomicS1, _, _ ←
S ≡ S1
!
5186assignablevecE, N, _, _, vecF, M, _, _ ←
!GNU vectors
E ≡ F
N ≡ M
→
true
5190assignableptr_, _, bool, _ ←
!
5191assignable_, null, ptr_, _ ←
!
5192assignableptrA, _, ptrB, Env ←
A = func_, _, _
B = func_, _, _
→
else
true
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
5212conditional_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.

5216armsA, 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 ?:.

5225arm_nullnessptr_, XV, Env, N ←
!
nullnessXV, Env, N
5226arm_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
5241assignedE, 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).

5250nullnessXV, Env, N ←
!
N = null
5251nullness_, _, 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
5272null_pointer_constantXV, Env ←
loweredXV, T
foldedXV, Env, 0
5274null_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
5310arms_typeTA, _, TB, _, T ←
5313arms_typevoid, _, _, _, void ←
!
5314arms_type_, _, void, _, void ←
!
5315arms_typeT, _, U, _, T ←
T ≡ U
T = struct_
or
T = union_
!
5316arms_typeptrP, _, _, null, ptrP ←
!
5317arms_type_, null, ptrP, _, ptrP ←
!
5318arms_typeptrP, _, I, _, ptrP ←
5319arms_typeI, _, ptrP, _, ptrP ←
5320arms_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.

5325targetqualifiedQs, U, Qs, U ←
!
5326targetU, [], U
pointed_type+A, +B, +Qs, -T

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

5330pointed_typeA, B, Qs, T ←
A ≡ void
or
B ≡ void
!
qualifyQs, void, T
5331pointed_typeA, B, Qs, T ←
!
qualifyQs, C, T
5332pointed_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
5362builtin_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.

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

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

5382literal_lengthcast_, E, N ←
5383literal_lengthstrBytes, N ←
lengthBytes, N
5384literal_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
5407type_class_numbervoid, 0
5408type_class_numberbool, 4 ←
!
5409type_class_numberT, 1 ←
5410type_class_numberT, 8 ←
5411type_class_numberptr_, 5
5412type_class_numberarray_, _, 5
5413type_class_numberfunc_, _, _, 5
5414type_class_numberstruct_, 12
5415type_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
5446types_compatible_valueTA, TB, Env, V ←
maplistcompared_type_nameEnv, TA, TB, A, B
→
V = 1
else
V = 0
5450compared_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
5479lock_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
5504'__builtin_assume'_num0, none−dec
5505'__builtin_prefetch'P|_P
5506'__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).

5521generic_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', []
5530generic_association_, _, assocdefault, E, assocdefault, E ←
!
5531generic_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
5539generic_unique[], _
5540generic_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'
5569elaborateE, 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'
5611faultE, Env, Format, Args ←
unsupportedE, Env, F, As
→
Format = F
Args = As
else
fault_E, Env, Format, Args
5616fault_idName, _, 'undeclared identifier ~w', Name
5617fault_label_addrName, _, 'the address of label ~w outside its function', Name
5618fault_indexA, I, Env, F, As ←
fault_underef, binadd, A, I, Env, F, As
5619fault_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
5631fault_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 = []
5637fault_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 = []
5642fault_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
5647fault_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
5654fault_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)'
5664fault_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'
5667fault_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
5679fault_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
5689fault_commaA, B, Env, F, As ←
operands_faultA, B, Env, F, As
5690fault_sizeof_exprE, Env, F, As ←
untypedE, Env
→
fault_E, Env, F, As
else
F = 'sizeof of a bit-field'
As = []
5694fault_binOp, A, B, Env, F, As ←
memberchkOp, land, lor
!
scalar_faultA, Env, F, As
→
true
else
scalar_faultB, Env, F, As
5699fault_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
5704fault_condC, A, B, Env, F, As ←
arms_faultA, B, Env, F, As
→
true
else
scalar_faultC, Env, F, As
5708fault_gcondC, B, Env, F, As ←
arms_faultC, B, Env, F, As
→
true
else
scalar_faultC, Env, F, As
5712fault_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).

5721arms_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.

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

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

5738scalar_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.

5746modifiable_faultobj_, _, What, '~w a read-only lvalue', What ←
!
5747modifiable_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.

5751assignment_fault=, TA, TB, 'cannot assign ~w to ~w', TB, TA ←
!
5752assignment_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.

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

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

5766arity_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.

5776arguments_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
5792unspecifiednoneunspecified
5793[]none[]
5794P|PsPPs
member_subject+XS

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

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

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

5803lowered_typeE, Env, T ←
operandE, Env, _, T0
5804lowered_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.

5808unsupportedE, Env, F, As ←
E = type_, _
!
type_name_typeE, T, Env, _
5812unsupportedE, 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.

5821missing_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