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

85:- discontiguousdoc/3
usesc_parsec_operator/4, compound_assignment/2

Environment

empty_env-Envdet

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

94empty_enveOrds, Tags, Defs, 0 ←
empty_assocOrds
empty_assocTags
list_to_assoc'builtin.va_list'−Def, Defs
100va_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.

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

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

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

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

140sema_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 one of none, static or extern. A typedef declares no Decls; Env is Env0 with its names bound instead. Env also has any struct, union or enum Specs defines. Throws not_constant when an enumerator's value is not constant.

155decl_typesSpecs, Inits, Decls, Env0, Env ←
specs_typeSpecs, Base, Env0, Env1
memberchkstorageStorage0, Specs
→
Storage = Storage0
else
Storage = none
foldldeclarator_typeSpecs, Base, Storage, Inits, Decls0, Env1, Env
exclude==none, Decls0, Decls
declarator_type+Specs, +Base, +Storage, +Init, -Decl, +Env0, -Env

Decl is the d/5 term Init declares, or none for a typedef, which binds the name.

165declarator_typeSpecs, Base, Storage, initdclsomeName, SyntaxType, Asm, Init, Decl, Env0, Env ←
apply_declaratorSyntaxType, Base, Type, Env0, Env1
reflect_typetypeSpecs, SyntaxType, Type, Env1, Reflected
bind_ordinaryqtypeName, Reflected, Env1, Env2
Storage ≡ typedef
→
bind_ordinaryName, typedefType, Env2, Env
Decl = none
else
Env = Env2
Decl = dName, Type, Storage, Asm, Init

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.

181reflect_typetypeSpecs, Syntax, Type, Env, Reflected ←
reflect_declaratorSyntax, Specs, Type, Env, Reflected
184reflect_declaratorbase, Specs, Type, Env, Reflected ←
memberchktnameName, Specs
lookup_ordinaryEnv, qtypeName, T
→
Base = T
else
Base = Type
findallQ, memberqualQ, Specs, Qs
qualifyQs, Base, Reflected
190reflect_declaratorptrS, Qs, Specs, ptrT, Env, R ←
reflect_declaratorS, Specs, T, Env, RT
qualifyQs, ptrRT, R
192reflect_declaratorarrayS, _, Specs, arrayT, N, Env, arrayRT, N ←
reflect_declaratorS, Specs, T, Env, RT
194reflect_declaratorfuncS, Params, Specs, funcT, Ps, V, Env, funcRT, RPs, V ←
reflect_declaratorS, Specs, T, Env, RT
reflect_param_typesParams, Ps, Env, RPs
197reflect_declarator_, _, badWhy, _, badWhy
199reflect_param_typesunspecified, unspecified, _, unspecified
200reflect_param_typesparamsParams, _, Ts, Env, RTs ←
maplistreflect_param_typeEnv, Params, Ts, RTs
203reflect_param_typeEnv, paramSpecs, dcl_, Syntax, _, T, RT ←
parameter_syntaxSyntax, Adjusted
reflect_typetypeSpecs, Adjusted, T, Env, R0
208parameter_syntaxarrayS, _, ptrS, [] ←
!
209parameter_syntaxfuncS, P, ptrfuncS, P, [] ←
!
210parameter_syntaxS, S
212qualifyQs0, qualifiedQs1, T, R ←
!
appendQs0, Qs1, Qs
qualifyQs, T, R
214qualifyQs, arrayT0, N, arrayT, N ←
Qs ≢ []C11 6.7.3p9
!
qualifyQs, T0, T
216qualifyQs0, T, R ←
sortQs0, Qs
Qs ≡ []
→
R = T
else
R = qualifiedQs, T
219reflect_paramsfunc_, paramsParams, _, Types, Env0, Env ←
!
foldlreflect_param, Params, Types, Env0, Env
221reflect_params_, _, Env, Env
223reflect_paramParam, T, Env0, Env ←
Param = param_, dclsomeName, _, _
!
reflect_param_typeEnv0, Param, T, RT
bind_ordinaryqtypeName, RT, Env0, Env
227reflect_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.

237specs_typeSpecs, Type, Env0, Env ←
base_typeSpecs, Type0, Env0, Env
memberSpec, Specs
→
Type = bad'alignment attributes'
else
Type = Type0
layout_attr+Spec

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

246layout_attralignas_
247layout_attrattrTokens ←
is_listTokens
attr_wordTokens, Word
memberchkWord, aligned, '__aligned__', packed, '__packed__'
!
attr_word+Tokens, -Wordnondet

Word is an identifier anywhere in an attribute's tokens, looking into groups.

255attr_wordTokens, Word ←
memberToken, Tokens
Token = idWord
or
Token = groupGroup
attr_wordGroup, Word
base_type+Specs, -Type, +Env0, -Env

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

264base_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_

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

283named_typetname'__builtin_va_list', arraystruct'builtin.va_list', 1, Env, Env ←
!
285named_typetnameName, Type, Env, Env ←
lookup_ordinaryEnv, Name, typedefType0
→
Type = Type0
else
Type = bad'unknown typedef'
289named_typetypeof_, badtypeof, Env, Env
290named_typesuKind, Tag, Members, Attrs, Type, Env0, Env ←
su_typeMembers, Kind, Tag, Attrs, Type, Env0, Env
292named_typeenumTag, Enumerators, Type, Env0, Env ←
enum_typeEnumerators, Tag, Type, Env0, Env
keyword_type+Keywords, -Type

the type a list of type keywords names.

296keyword_type[], int

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

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

304keyword_combovoid, void
305keyword_combobool, bool
306keyword_combochar, char
307keyword_combochar, signed, schar
308keyword_combochar, unsigned, uchar
309keyword_comboS, short ←
memberS, short, short, signed, int, short, int, short, signed
311keyword_comboS, ushort ←
memberS, short, unsigned, int, short, unsigned
313keyword_comboS, int ←
memberS, int, signed, int, signed
315keyword_comboS, uint ←
memberS, unsigned, int, unsigned
317keyword_comboS, long ←
memberS, long, long, signed, int, long, int, long, signed
319keyword_comboS, ulong ←
memberS, long, unsigned, int, long, unsigned
321keyword_comboS, llong ←
memberS, long, long, long, long, signed, int, long, long, int, long, long, signed
324keyword_comboS, ullong ←
memberS, long, long, unsigned, int, long, long, unsigned
326keyword_combofloat, float
327keyword_combodouble, double
328keyword_combodouble, long, ldouble
329keyword_comboS, bad'_Complex' ←
memberchkcomplex, S
330keyword_comboS, bad'__int128' ←
memberchkint128, 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.

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

353su_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
layout_attrattrAttr
→
Def = bad'alignment attributes'
memberchkbadWhy, Fields
→
known_field_prefixFields, Prefix
su_layoutKind, Prefix, Env2, PrefixDef
PrefixDef = def_, Known, _, _
→
Def = partialWhy, Known
else
Def = badWhy
else
su_layoutKind, Fields, Env2, Def
Env2 = eOrds, Tags2, Defs0, Next
put_assocId, Defs0, Def, Defs1
put_assocqmembersId, Defs1, Reflected, Defs2
Env = eOrds, Tags2, Defs2, Next

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.

381known_field_prefix[], []
382known_field_prefixbad_|_, [] ←
!
383known_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.

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

399tag_boundnone, _, Tags, Tags
400tag_boundsomeName, Value, Tags0, Tags ←
put_assocName, Tags0, Value, Tags
member_decl+Decl, -Fields, -Reflected, +Env0, -Env

Fields are the f(Name, Type) or bit-fields b(Name, Type, Width) the member declaration Decl declares ([] for a static assertion), or [bad(Why)]; Name is some(Field), or none for an anonymous member or an unnamed bit-field. Reflected are the q(Name, QType) of the named members, QType keeping the qualifiers that Type drops.

408member_declstatic_assert_, _, _, [], [], Env, Env
409member_declfieldSpecs, dclName, SyntaxType, _, Width, Fields, Reflected, Env0, Env ←
specs_typeSpecs, Base, Env0, Env1
apply_declaratorSyntaxType, Base, Type, Env1, Env
bad_typeType, Why
→
Fields = badWhy
Reflected = []
Width ≢ none
¬bitfield_widthWidth, Name, Type, Env, _
→
Fields = bad'invalid bit-field'
Reflected = []
else
Width ≡ none
→
Fields = fName, Type
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
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).

429bitfield_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, +Fields, +Env, -Def), with 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.

443su_layoutKind, Fields, Env, Def ←
catchfields_layoutKind, Fields, Env, Layout, badWhy, Layout = badWhy
layout_defLayout, Kind, Def
fields_layout+Kind, +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.

451fields_layoutKind, Fields, Env, Layout ←
foldlfield_memberKind, Env, Fields, Memberss, 0−1, EndBits−Align
→
appendMemberss, Members
End is EndBits + 7//8
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.

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

472field_memberKind, Env, fName, Type, mName, Type, Off, End0−Align0, End−Align ←
Type = arrayElemType, none
→
type_size_alignElemType, Env, _, FieldAlign
FieldSize = 0
else
type_size_alignType, Env, FieldSize, FieldAlign
member_offset_endKind, End0, FieldSize, FieldAlign, Off, End
Align is maxAlign0, FieldAlign
481field_memberKind, 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
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.

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

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

518enum_type[], Tag, Type, Env0, Env ←
enum_definition[], Tag, Type, Env0, Env
520enum_typeEnumerator|Enumerators, Tag, Type, Env0, Env ←
enum_definitionEnumerator|Enumerators, Tag, Type, Env0, Env
522enum_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.

532enum_definitionEnumerators, Tag, Type, Env0, Env ←
foldlenumerator, Enumerators, Values, Env0−0, Env1−_
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.

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

559enum_underlying[], uint
560enum_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

Declarators

apply_declarator+SyntaxType, +Base, -Type, +Env0, -Envdet

Type is the declarator type SyntaxType with the atom base replaced by Base, array sizes evaluated and parameter types resolved. The parser builds declarator types outermost first around base. An array size that is not a non-negative constant gives bad(Reason). Env is Env0 with the definitions of any struct or union a parameter list declares; their names are scoped to the list.

579apply_declaratorbase, Base, Base, Env, Env
580apply_declaratorptrSyntax, _, Base, ptrType, Env0, Env ←
apply_declaratorSyntax, Base, Type, Env0, Env
582apply_declaratorarraySyntax, SizeExpr, Base, Type, Env0, Env ←
apply_declaratorSyntax, Base, ElemType, Env0, Env
SizeExpr ≡ none
→
Type = arrayElemType, none
catchconst_valueSizeExpr, Env, N, _, not_constant, fail
N ≥ 0
→
Type = arrayElemType, N
else
Type = bad'non-constant array size'
590apply_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.

598param_typesunspecified, unspecified, fixed, Env, Env
599param_typesparamsParams, Variadic, Types, Variadic, Env0, Env ←
env_scopeEnv0, Scope, _
foldlparam_type, Params, Types, Env0, Env1
env_restoreScope, Env1, Env
604param_typeparamSpecs, dcl_, SyntaxType, _, Type, Env0, Env ←
specs_typeSpecs, Base, Env0, Env1
apply_declaratorSyntaxType, Base, Type0, Env1, Env
type_adjustedType0, Type
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.

612type_adjustedarrayT, _, ptrT ←
!
613type_adjustedfuncR, P, V, ptrfuncR, P, V ←
!
614type_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.

621type_name_typetypeSpecs, SyntaxType, Type, Env0, Env ←
specs_typeSpecs, Base, Env0, Env1
apply_declaratorSyntaxType, Base, Type, Env1, Env
vm_type+Type

Type is variably modified (6.7.6p3), which nanofil does not support. The cut ending the func clause succeeds once however many parts are variably modified.

628vm_typebad'non-constant array size'
629vm_typeptrType ←
630vm_typearrayType, _ ←
631vm_typefuncRet, Params, _ ←
or
is_listParams
memberParam, Params
vm_typeParam
!
unsupported_type+T, -Format, -Argssemidet
Tina type an expression or declaration names
Formatoutthe diagnostic's format
Argsoutits arguments

nanofil does not support T: it is variably modified, since its size expressions would have to be evaluated, side effects and all, or it has no values nanofil can hold.

examples 2/2 hold
✓unsupported_typeldouble, '~w is not supported', 'long double'
✓unsupported_typeptrldouble, _, _⟹fails
649unsupported_typeT, 'variably modified types (non-constant array size) are not supported', [] ←
651unsupported_typeT, '~w is not supported', Why ←
unsupported_value_type+Type, -Why

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

656unsupported_value_typeldouble, 'long double'
657unsupported_value_typebadWhy, Why
658unsupported_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).

662bad_typebadWhy, Why
663bad_typearrayT, _, Why ←
bad_typeT, Why
664bad_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.

677type_size_alignType, Env, Size, Align ←
size_align_Type, Env, Size0, Align0
→
Size = Size0
Align = Align0
else
throwbadincomplete
682size_align_qualified_, T, Env, Size, Align ←
type_size_alignT, Env, Size, Align
684size_align_bool, _, 1, 1
685size_align_char, _, 1, 1
686size_align_schar, _, 1, 1
687size_align_uchar, _, 1, 1
688size_align_short, _, 2, 2
689size_align_ushort, _, 2, 2
690size_align_int, _, 4, 4
691size_align_uint, _, 4, 4
692size_align_long, _, 8, 8
693size_align_ulong, _, 8, 8
694size_align_llong, _, 8, 8
695size_align_ullong, _, 8, 8
696size_align_float, _, 4, 4
697size_align_double, _, 8, 8
698size_align_ldouble, _, 16, 16
699size_align_ptr_, _, 8, 8
700size_align_arrayT, N, Env, Size, Align ←
integerN
type_size_alignT, Env, ElemSize, Align
Size is ElemSize × N
704size_align_structId, Env, Size, Align ←
su_sizeId, Env, Size, Align
705size_align_unionId, Env, Size, Align ←
su_sizeId, Env, Size, Align
706size_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.

710su_sizeId, e_, _, Defs, _, Size, Align ←
get_assocId, Defs, Def
def_size_alignDef, Size, Align
714def_size_aligndef_, _, Size, Align, Size, Align
715def_size_alignpartialWhy, _, _, _ ←
throwbadWhy
717def_size_alignbadWhy, _, _ ←
throwbadWhy
complete_type+Type, +Envsemidet

Type is complete in Env: it has a size.

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

731member_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
775member_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
786named_membermsomeName, Type, Off, Name, Type, Off, none
787named_membermsomeName, Type, Off, Bits, Name, Type, Off, Bits
789known_membersdef_, Members, _, _, Members
790known_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
805int_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
818bool1unsigned0
819char1signed1
820schar1signed1
821uchar1unsigned1
822short2signed2
823ushort2unsigned2
824int4signed3
825uint4unsigned3
826long8signed4
827ulong8unsigned4
828llong8signed5
829ullong8unsigned5
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
838int_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
845rankType, 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
855unsigned_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
866scalar_typeT ←
float_type?Typenondet
Typein/outa real floating type

The floating types nanofil supports; long double is refused.

examples 1/1 hold
✓float_typeldouble⟹fails
874float_typefloat
875float_typedouble
arithmetic_type+Typesemidet
Typeina type term

Type is an integer or floating type.

examples 2/2 hold
✓arithmetic_typebool
✓arithmetic_typeptrint⟹fails
884arithmetic_typeT ←
885arithmetic_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
897default_promotionfloat, double ←
!
898default_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
912integer_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 8/8 hold
✓usual_arith_conversionint, double, T⟹T = double
✓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
935usual_arith_conversionTypeA0, TypeB0, Type ←
TypeA0 ≡ double
or
TypeB0 ≡ double
!
Type = double
939usual_arith_conversionTypeA0, TypeB0, float ←
TypeA0 ≡ float
or
TypeB0 ≡ float
!
942usual_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
975convert_intV, bool, W ←
!
V =:= 0
→
W = 0
else
W = 1
976convert_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
990signed_wordsigned, U, M, W ←
U ≥ M >> 1
→
W is U−M
else
W = U
991signed_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. Deterministic when Type is bound.

examples 5/5 hold
✓type_classushort, K⟹K = w
✓type_classullong, K⟹K = l
✓type_classptrvoid, K⟹K = p
✓type_classfloat, K⟹K = s
✓type_classstruct0, _⟹fails
1005type_classT, K ←
integer_typeT, Size, _, _
Size =:= 8
→
K = l
else
K = w
1006type_classfloat, s
1007type_classdouble, d
1008type_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.

1027const_valueExpr, Env, V, T ←
constant_valueExpr, Env, V, T
→
true
else
thrownot_constant

Float constants use their unsigned IEEE bits, never a host float.

1032constant_valueExpr, Env, V, T ←
const_evalExpr, Env, V0, T0
integerV0
→
V = V0
T = T0
else
thrownot_constant
1037float_literalCs, Bits, Type ←
appendBody, Suffix, Cs
memberchkSuffix, f, 'F'
→
Type = float
Digits = Body
memberchkSuffix, l, 'L'
→
sema_errorconstant, 'long double is not supported', []
else
Type = double
Digits = Cs
type_classType, K
ieee:ieee_parseK, Digits, Bits

glibc's HUGE_VAL and INFINITY are builtin constant expressions, not libm calls. Keep the value bits exact, including in static storage.

float_builtin
1048'__builtin_huge_val'double0x7ff0000000000000
1049'__builtin_inf'double0x7ff0000000000000
1050'__builtin_huge_valf'float0x7f800000
1051'__builtin_inff'float0x7f800000

C arithmetic conversion of a constant. An undefined float-to-int conversion propagates ub, just like integer division by zero.

1055const_convertub, _, _, ub ←
!
1056const_convertV, T, T, V ←
!
1057const_convertV, From, bool, W ←
!
const_truthV, From, W
1058const_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
1073integer_rangesigned, Bits, Lo, Hi ←
Lo is −1 << Bits−1
Hi is −Lo−1
1074integer_rangeunsigned, Bits, 0, Hi ←
Hi is 1 << Bits−1
1076const_truthub, _, ub ←
!
1077const_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.

1086pp_valueExpr, V ←
const_evalExpr, pp, V, _
integerV
const_eval+Expr, +Env, -V, -T

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.

1093const_evalExpr, Env, V, T ←
elaborateExpr, Env, X
valueX, XV
loweredXV, T
foldedXV, Env, V

folded(+XV, +Env, -V): the value tree 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.

1101foldedval_, intV, _, V

6.4.4.1, 6.4.4.4

1102foldedvalT, floatCs, _, V ←

6.4.4.2

1103foldedval_, builtinName, [], _, V ←
float_builtinName, _, V
1104foldedval_, unnot, XV, Env, V ←
6.5.3.3p5
operand_valueXV, Env, V0, T0
const_truthV0, T0, B
definedB, V, truthB =:= 0, V
1108foldedvalT0, unOp, XV, Env, V ←
6.5.3.3p2-4
Op ≢ not
operand_valueXV, Env, V0, _
definedV0, V, constant_unaryT, Op, V0, V
1113foldedvalT0, 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
1119foldedvalT0, 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
1128foldedvalT0, cast_, XV, Env, V ←
6.5.4p5
operand_valueXV, Env, V0, From
const_convertV0, From, T, V
1132foldedval_, sizeoftypeSpecs, Decl, Env, V ←
!6.5.3.4p2
type_name_typetypeSpecs, Decl, T, Env, _
catchtype_size_alignT, Env, V, _, bad_, fail
1135foldedval_, sizeofX, Env, V ←
catchtype_size_alignT, Env, V, _, bad_, fail
1138foldedval_, alignofTypeName, Env, V ←
6.5.3.4p3
type_name_typeTypeName, T, Env, _
catchtype_size_alignT, Env, _, V, bad_, fail
1141foldedval_, offsetofTypeName, Path, Env, V ←
7.19p3
type_name_typeTypeName, T, Env, _
offset_pathPath, T, Env, 0, V
1144foldedval_, comma_, XV, Env, V ←
foldedXV, Env, V
operand_value+XV, +Env, -V, -T

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

1148operand_valueXV, Env, V, T ←
foldedXV, Env, V
loweredXV, T
1150constant_unaryT, Op, A, V ←
→
Op ≡ plus
→
V = A
Op ≡ neg
→
ieee:ieee_negK, A, V
else
fail
else
unary_arithOp, A, R
offset_path+Path, +Type, +Env, +Off0, -Off

offsetof's member path.

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

1174env_int_typee_, _, _, _, T, T
1175env_int_typepp, T0, T ←
int_infoT0, _, Sign
1179intmax_typesigned, long
1180intmax_typeunsigned, ulong
defined+Values, -V, :Goal

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

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

V is the value V0 converted to T.

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

B is 1 if Goal holds, else 0.

1191truthGoal, 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
1195negX−X
1196plusXX
1197bnotX\X
binary_kind?Op, ?Kind

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

1201binary_kindOp, Kind ←
c_operator_, _, Op, Kind0
Kind0 = arith_
→
Kind = arith
else
Kind = Kind0
kind_binary+Kind, +Op, +A, +B, +T, -V

V, of type T, is the value of Op, of Kind, on operands A and B, each Value-Type. Arithmetic converts both to T, a comparison both to their common type (binary_operands/6), and a shift only the left one to T.

1209kind_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 ; const_arithOp, A, B, R, int_valueR, T, V
1219kind_binaryshift, Op, VA−_, VB−_, T, V ←
definedVA, VB, V, convert_intVA, T, A, const_shiftOp, A, VB, R, convert_intR, T, V
1225kind_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
1235kind_binarylogical, Op, VA−TA, VB−TB, _, V ←
const_truthVA, TA, A
const_truthVB, TB, B
definedA, V, const_logicalOp, A, B, V
1239const_shiftshl, A, B, R ←
R is A << B
1240const_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.

1244const_logicalland, A, VB, V ←
A =:= 0
→
V = 0
else
definedVB, V, truthVB ≠ 0, V
1246const_logicallor, A, VB, V ←
A ≠ 0
→
V = 1
else
definedVB, V, truthVB ≠ 0, V

Division by zero is undefined: R is ub.

1250const_arithadd, A, B, R ←
R is A + B
1251const_arithsub, A, B, R ←
R is A−B
1252const_arithmul, A, B, R ←
R is A × B
1253const_arithdiv, A, B, R ←
B =:= 0
→
R = ub
else
R is A//B
1254const_arithmod, A, B, R ←
B =:= 0
→
R = ub
else
R is A rem B
1255const_arithand, A, B, R ←
R is A /\ B
1256const_arithor, A, B, R ←
R is A \/ B
1257const_arithxor, A, B, R ←
R is A xor B
1259const_compareeq, A, B ←
A =:= B
1260const_comparene, A, B ←
A ≠ B
1261const_comparelt, A, B ←
A < B
1262const_comparegt, A, B ←
A > B
1263const_comparele, A, B ←
A ≤ B
1264const_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.

1271num_typeV, Suffix−Radix, Type ←
constant_typesRadix, Suffix, Types
memberType, Types
fitsV, Type
→
true
else
Type = ullong
constant_types
1279decnoneint, long, llong
1280decuuint, ulong, ullong
1281decllong, llong
1282decululong, ullong
1283decllllong
1284decullullong
1285othernoneint, uint, long, ulong, llong, ullong
1286otheruuint, ulong, ullong
1287otherllong, ulong, llong, ullong
1288otherululong, ullong
1289otherllllong, ullong
1290otherullullong
fits+V, +Type

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

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

1300below_maxsigned, V, Bits ←
V < 1 << Bits−1
1301below_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
1318compatible_typeT, U ←
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 function's parameters where either has a prototype. Fails when T and U are not compatible.

examples 4/4 hold
✓composite_typeptrarrayint, none, ptrarrayint, 3, C⟹C = ptrarrayint, 3
✓composite_typefuncint, unspecified, fixed, funcint, ptrchar, fixed, C⟹C = funcint, ptrchar, fixed
✓composite_typearrayint, 2, arrayint, 3, _⟹fails
✓composite_typefuncint, [], fixed, funcint, [], variadic, _⟹fails
1336composite_typeT, U, T ←
T ≡ U
!
1337composite_typequalifiedQs, T, qualifiedQs, U, qualifiedQs, C ←
1339composite_typeptrT, ptrU, ptrC ←
1340composite_typearrayT, N, arrayU, M, arrayC, K ←
1343composite_typefuncR, Ps, V, funcS, Qs, V, funcC, Cs, V ←
composite_size+N, +M, -K

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

1349composite_sizenone, M, M ←
!
1350composite_sizeN, none, N ←
!
1351composite_sizeN, N, N
composite_params+Ps, +Qs, -Cs

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

1355composite_paramsunspecified, Qs, Qs ←
!
1356composite_paramsPs, unspecified, Ps ←
!
1357composite_paramsPs, Qs, Cs ←
maplistcomposite_type, Ps, Qs, Cs
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
1368unqualifiedT, T ←
varT
!
1369unqualifiedqualified_, T0, T ←
1370unqualifiedptrT0, ptrT ←
1371unqualifiedarrayT0, N, arrayT, N ←
1372unqualifiedfuncR0, Ps0, V, funcR, Ps, V ←
!
Ps0 ≡ unspecified
→
Ps = Ps0
else
maplistunqualified, Ps0, Ps
1375unqualifiedT, 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.

examples 12/12 hold
✓
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
1433elaborateE, Env, X ←
elabE, Env, X0
!
X = X0
1435elabidName, Env, X ←
lookup_ordinaryEnv, Name, Binding
!
identifier_typeBinding, Name, Env, T
binding_nodeBinding, T, Name, X
1439elabidName, _, objarraychar, none, predefinedName ←
6.4.2.2
memberchkName, '__func__', '__FUNCTION__', '__PRETTY_FUNCTION__'
1441elabnumV0, S, Env, valT, intV ←
num_typeV0, S, T0
env_int_typeEnv, T0, T
convert_intV0, T, V
1443elabchrV, Env, valT, intV ←
env_int_typeEnv, int, T
1444elabfloatCs, Env, valT, floatCs ←
lastCs, Suffix
memberchkSuffix, f, 'F'
→
T0 = float
memberchkSuffix, l, 'L'
→
T0 = ldouble
else
T0 = double
env_int_typeEnv, T0, T
1451elabstrBytes, _, objarraychar, N, strBytes ←
lengthBytes, M
N is M + 1
1453elabwstrT, Values, _, objarrayT, N, wstrT, Values ←
lengthValues, M
N is M + 1
1455elabcastTN, E, Env, valT, castTN, XV ←
type_name_typeTN, T0, Env, _
operandE, Env, XV, F0
reflect_typeTN, T0, Env, T
1460elabcomplitTN, Init, Env, objT, complitTN, Init ←
type_name_typeTN, T0, Env, _
reflect_typeTN, T0, Env, T
1462elabunaddr, E, Env, valptrT, addrX ←
6.5.3.2p1
elaborateE, Env, X
X ≠ val_, _
X ≠ obj_, bitfield_, _
1465elabunderef, E, Env, X ←
operandE, Env, XV, ptrT
designatorT, derefXV, X
1468elabunOp, E, Env, valT, incdecOp, X ←
6.5.3.1p1
memberchkOp, preinc, predec, postinc, postdec
elaborateE, Env, X
valueX, XV
1472elabunOp, E, Env, valT, unOp, XV ←
memberchkOp, plus, neg, bnot, not
operandE, Env, XV, T1
unary_operandOp, T1, T0
env_int_typeEnv, T0, T
1476elabindexA, I, Env, X ←
6.5.2.1p2
elabunderef, binadd, A, I, Env, X
1478elabmemberE, 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
1485elabarrowE, F, Env, X ←
6.5.2.3p4
elabmemberunderef, E, F, Env, X
1487elabcallidName, Args, Env, valT, builtinName, Args ←
¬lookup_ordinaryEnv, Name, _
Args ≡ []
float_builtinName, T, _
→
true
else
1492elabcallE, 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
1497elabassignOp, A, B, Env, valT, assignOp, X, XB ←
6.5.16p2
elaborateA, Env, X
argumentEnv, B, XB
loweredXB, TB
assignsOp, TA, XB, TB, Env
1502elabcommaA, B, Env, valT, commaXA, XV ←
argumentEnv, A, XA
operandB, Env, XV, T
1505elabsizeof_exprE, Env, valulong, sizeofX ←
6.5.3.4p1
elaborateE, Env, X
X ≠ obj_, bitfield_, _
1507elabsizeof_typeTN, _, valulong, sizeofTN
1508elabalignofTN, _, valulong, alignofTN
1509elaboffsetofTN, Path, _, valulong, offsetofTN, Path
1510elabbinOp, A, B, Env, valT, binOp, XA, XB ←
operandA, Env, XA, TA
operandB, Env, XB, TB
env_int_typeEnv, T0, T
1513elabcondC, A, B, Env, valT, condXC, XA, XB ←
6.5.15p2
argumentEnv, C, XC
tree_typeXC, TC
armsA, B, Env, XA, XB, T
1516elabgcondC, B, Env, valT, gcondXC, XB ←
armsC, B, Env, XC, XB, T
tree_typeXC, TC
1518elabva_argA, TN, Env, valT, va_argTN, XA ←
argumentEnv, A, XA
type_name_typeTN, T0, Env, _
reflect_typeTN, T0, Env, T
1521elabstmtexpratLoc, blockItems, Env, valT, stmtexpratLoc, blockItems ←
block_value_typeItems, Env, T
1523elabgenericC, As, Env, X ←
6.5.1.1
operandC, Env, _, CT
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.

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

1535assignment_typeobjT0, bitfield_, W, T ←
1537assignment_typeX, T ←
lowered+XV, -T

T is the type of value XV without qualifiers, the type lowering gives it.

1541loweredXV, 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).

1546assigns=, TA, XB, TB, Env ←
!
nullnessXB, Env, N
assignableTB, N, TA
1547assignsOp, TA, _, TB, _ ←
binary_operandsBinOp, TA, TB, _, _, T
assignableT, value, TA
arity_fits+Ps, +V, +Args

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

1553arity_fitsunspecified, _, _ ←
!
1554arity_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.

1560passesunspecified, _, _ ←
!
1561passes[], _, _
1562passesP|Ps, X|Xs, Env ←
nullnessX, Env, N
assignableT, N, TP
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
1583identifier_typeenumconst_, T0, _, _, T ←
!
T = T0
1584identifier_typeBinding, Name, Env, T ←
binding_typeBinding, T0
lookup_ordinaryEnv, qtypeName, RT
→
else
T = T0
binding_type
1590var_, TT
1591gvarT, _, _, _T
1592fnT, _, _T
1593enumconst_, TT
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.

1598reflected_shapequalifiedQs, R, T, qualifiedQs, S ←
1600reflected_shapearrayR, _, arrayT, N, arrayS, N ←
1602reflected_shapeR, _, R
binding_node+Binding, +T, +Name, -X

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

binding_node
1606var_, _TNameobjT, idName
1607gvar_, _, _, _TNameobjT, idName
1608fn_, _, _TNamefunT, idName
1609enumconstV, _T_valT, intV
designator+T, +F, -X

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

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

1618member_nodeobjTS, FS, F, T, objT, memberobjTS, FS, F
1619member_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
1637member_typequalifiedQs, SU, F, Env, T ←
!
member_typeSU, F, Env, T0
qualifyQs, T0, T
1639member_typeSU, F, Env, T ←
SU =.. _, Id
Env = e_, _, Defs, _
get_assocqmembersId, Defs, Members
!
members_typeMembers, F, Env, T
1643member_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.

1648members_typeMembers, F, _, T ←
memberchkqsomeF, T0, Members
!
T = T0
1650members_typeMembers, F, Env, T ←
memberqnone, Anon, Members
member_typeAnon, F, Env, T
!
builtin_type?Name, ?Type

a call of builtin Name, when Name is not declared, has Type.

1655builtin_type'__builtin_va_start', void
1656builtin_type'__builtin_va_copy', void
1657builtin_type'__builtin_va_end', void
1658builtin_type'__builtin_unreachable', void
1659builtin_type'__builtin_alloca', ptrvoid
1660builtin_type'__builtin_expect', long
1661builtin_type'__builtin_constant_p', int
1662builtin_typeName, bool ←
overflow_builtin?Name, ?Op, ?Fixed

builtin Name checks arithmetic Op on operands of type Fixed, or of any integer types (generic).

1666overflow_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.

1679block_value_type[], _, void
1680block_value_typeItem, Env, T ←
!
last_item_typeItem, Env, T
1681block_value_typedecl_, Specs, Inits|Items, Env0, T ←
!
decl_typesSpecs, Inits, Decls, Env0, Env1
foldlbind_decl, Decls, Env1, Env
block_value_typeItems, Env, T
1685block_value_type_|Items, Env, T ←
block_value_typeItems, Env, T
1687bind_decldName, Type, _, _, _, Env0, Env ←
bind_ordinaryName, varnone, Type, Env0, Env
1690last_item_typeat_, exprE, Env, T ←
!
operandE, Env, _, T
1691last_item_typeat_, label_, Body, Env, T ←
!
last_item_typeBody, Env, T
1692last_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
1701tree_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
1710obj_, _lvalue
1711fun_, _function
1712val_, _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
1730valueobjT, bitfieldM, W, valV, loadobjT, bitfieldM, W ←
1732valueobjT, F, valV, decayobjT, F ←
1733valueobjT, F, valV, loadobjT, F ←
1734valuefunT, F, valptrT, decayfunT, F
1735valuevalT, 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
1754bitfield_promotionT, W, P ←
int_infoT, _, Sign
Sign ≡ signed
→
W ≤ 32
else
W < 32
→
P = int
W ≤ 32
→
P = uint
else
P = T
1761array_typequalified_, T ←
1762array_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
1773value_typequalified_, T, R ←
1774value_typearrayT, _, ptrT ←
!
1775value_typefuncR, P, V, ptrfuncR, P, V ←
!
1776value_typeT, T
operand+E, +Env, -XV, -T

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

1780operandE, 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
1792expression_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
1809expression_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
1827modifiable_lvalueE, Env ←
elaborateE, Env, X
modifiable+X, +Env

typed tree X designates a modifiable object.

1830modifiableobjT, _, Env ←
1832modifiable_typequalifiedQs, T, Env ←
!
¬memberchkconst, Qs
1834modifiable_typearray_, _, _ ←
!
fail
1835modifiable_typevoid, _ ←
!
fail
1836modifiable_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.

1840const_withinqualifiedQs, _, _ ←
memberchkconst, Qs
!
1841const_withinqualified_, T, Env ←
1842const_withinarrayT, _, Env ←
1843const_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
1864unary_operandOp, T0, T ←
memberchkOp, plus, neg
1866unary_operandbnot, T0, T ←
1867unary_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).

1871expression_binary_typeOp, A, B, int ←
6.5.13p2, 6.5.14p2
memberchkOp, land, lor
!
1874expression_binary_typeOp, A, B, T ←
binary_operandsOp, A, B, _, _, T
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
1909binary_operandsOp, TA, TB, CA, CB, CA ←
memberchkOp, shl, shr
!
1912binary_operandsOp, TA, TB, C, C, T ←
callDomain, TA
callDomain, TB
!
→
T = int
else
T = C
1917binary_operandsOp, ptrP, TB, ptrP, long, ptrP ←
memberchkOp, add, sub
!
1919binary_operandsadd, TA, ptrP, long, ptrP, ptrP ←
1920binary_operandssub, ptrP, ptr_, ptrP, ptrP, long ←
!
1921binary_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.

1926arithmetic_operatorOp, Domain ←
c_operator_, _, Op, Kind
operand_domainKind, Domain
operand_domain
1930arithintegerint_type
1931aritharithmeticarithmetic_type
1932comparearithmetic_type
1934comparisonOp ←
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.

examples 6/6 hold
✓castablestruct1, void
✓castableptrchar, long
✓castablestruct1, struct1
✓castabledouble, ptrint⟹fails
✓castableptrint, float⟹fails
✓castableint, struct1⟹fails
1951castable_, void ←
!
1953castableptr_, B ←
B = ptr_
or
!
1954castableA, ptr_ ←
1955castableT, U ←
T ≡ U
T = struct_
or
T = union_
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.

examples 7/7 hold
✓assignabledouble, value, char
✓assignableptrint, value, bool
✓assignableint, null, ptrint
✓assignableptrint, value, ptrchar
✓assignableint, value, ptrint⟹fails
✓assignableptrfuncint, [], fixed, value, ptrfuncvoid, [], fixed⟹fails
✓assignablestruct1, value, struct2⟹fails
1977assignableFrom, _, To ←
1978assignableT, _, U ←
T ≡ U
!
1979assignableptr_, _, bool ←
!
1980assignable_, null, ptr_ ←
!
1981assignableptrA, _, ptrB ←
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 2/2 hold
✓
bind_ordinaryp, varnone, ptrchar, E0, E
conditional_typechr0−idp, E, T
⟹T = ptrchar
✓
bind_ordinaryp, varnone, ptrchar, E0, E
conditional_typechr1−idp, E, _
⟹fails
1997conditional_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.

2001armsA, B, Env, XA, XB, T ←
armA, Env, XA, TA, NA
armB, Env, XB, TB, NB
arms_typeTA, NA, TB, NB, T
2005armE, Env, XV, T, N ←
operandE, Env, XV, T
nullnessXV, Env, N
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
2022assignedE, Env, To, Loc ←
elaborateE, Env, X
valueX, XV
loweredXV, T
nullnessXV, Env, N
assignableT, N, To
→
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).

2031nullnessXV, Env, N ←
!
N = null
2032nullness_, _, 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
2053null_pointer_constantXV, Env ←
loweredXV, T
foldedXV, Env, 0
2055null_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 a nonnull integer are rejected.

examples 9/9 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, int, value, _⟹fails
✓arms_typestruct1, value, struct2, value, _⟹fails
2086arms_typeTA, _, TB, _, T ←
2089arms_typevoid, _, _, _, void ←
!
2090arms_type_, _, void, _, void ←
!
2091arms_typeT, _, U, _, T ←
T ≡ U
T = struct_
or
T = union_
!
2092arms_typeptrP, _, _, null, ptrP ←
!
2093arms_type_, null, ptrP, _, ptrP ←
!
2094arms_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.

2099targetqualifiedQs, U, Qs, U ←
!
2100targetU, [], U
pointed_type+A, +B, +Qs, -T

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

2104pointed_typeA, B, Qs, T ←
A ≡ void
or
B ≡ void
!
qualifyQs, void, T
2105pointed_typeA, B, Qs, T ←
!
qualifyQs, C, T
2106pointed_type_, _, _, void

Generic selection

The controlling expression undergoes lvalue/array/function conversion, not integer promotion. Only the selected expression is evaluated, and its value category is retained. Association types must be unique even when they do not match the controlling type.

2114generic_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', []
2123generic_association_, _, assocdefault, E, assocdefault, E ←
!
2124generic_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
2132generic_unique[], _
2133generic_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'
2162elaborateE, 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 or a long double constant in E that nanofil does not support. Fails when it is not one the elaborator diagnoses.

examples 9/9 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'
✓
faultcasttypetspecint, base, float'1', '.', '0', 'L', E, F, A
⟹F = 'long double is not supported', A = []
2207faultE, Env, Format, Args ←
unsupportedE, Env, F, As
→
Format = F
Args = As
else
fault_E, Env, Format, Args
2212fault_idName, _, 'undeclared identifier ~w', Name
2213fault_indexA, I, Env, F, As ←
fault_underef, binadd, A, I, Env, F, As
2214fault_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
2226fault_memberE, Field, Env, F, As ←
untypedE, Env
→
fault_E, Env, F, As
→
F = 'no member named ~w'
As = Field
else
F = 'member access on a non-lvalue'
As = []
2232fault_arrowE, Field, Env, F, As ←
untypedE, Env
→
fault_E, Env, F, As
lowered_typeE, Env, ptr_
→
F = 'no member named ~w'
As = Field
else
F = '-> on a non-pointer'
As = []
2237fault_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
2242fault_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
2254fault_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
2264fault_commaA, B, Env, F, As ←
operands_faultA, B, Env, F, As
2265fault_sizeof_exprE, Env, F, As ←
untypedE, Env
→
fault_E, Env, F, As
else
F = 'sizeof of a bit-field'
As = []
2269fault_binOp, A, B, Env, F, As ←
memberchkOp, land, lor
!
scalar_faultA, Env, F, As
→
true
else
scalar_faultB, Env, F, As
2274fault_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
2279fault_condC, A, B, Env, F, As ←
arms_faultA, B, Env, F, As
→
true
else
scalar_faultC, Env, F, As
2283fault_gcondC, B, Env, F, As ←
arms_faultC, B, Env, F, As
→
true
else
scalar_faultC, Env, F, As
2287fault_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).

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

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

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

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

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

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

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

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

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

2351arguments_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
¬assignableT, N, TP
→
F = 'cannot assign ~w to ~w'
As = T, TP
else
TP ≠ struct_
TP ≠ union_
arguments_faultPs1, Args, Env, F, As
param_split
2367unspecifiednoneunspecified
2368[]none[]
2369P|PsPPs
member_subject+XS

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

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

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

2378lowered_typeE, Env, T ←
operandE, Env, _, T0
2379lowered_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, or long double constant, and why.

2383unsupportedE, Env, F, As ←
E = type_, _
!
type_name_typeE, T, Env, _
2387unsupportedfloatCs, _, 'long double is not supported', [] ←
lastCs, S
memberchkS, l, 'L'
!
2389unsupportedE, Env, F, As ←
compoundE
E =.. _|Xs
memberX, Xs
unsupportedX, Env, F, As
!