~sebsite/hare-c

C implementation written in Hare
17 days ago
17 days ago

#hare-c

C parsing and checking library for Hare

A sprite in Scratch of a hare, named "Hare-c"

#Stuff in this repo

  • c - the c:: Hare module (and submodules)
  • cmd/cdecl - WIP-ish converter from C gibberish to readable English, and vice-versa. depends on madeline
  • cmd/crepl - C interpreter/REPL. depends on madeline
  • cmd/hacc - C compiler. depends on qbe
  • cmd/hareconv - converter between C headers and Hare modules
  • docs - man pages and implementation documentation

#Known Issues

An unordered incomplete list of miscellaneous stuff which doesn't work or isn't correctly implemented; if you want to contribute, tackling something on this list would be very appreciated :)

  • string concatenation ("\0 "0") is broken
  • typedef redeclarations should require that the underlying type specified is exactly the same as in the previous declaration, not just compatible
  • the check context should have a type store from which types are borrowed from; this would avoid a fuckton of allocations, and probably make the above typedef thing a hell of a lot easier to implement
  • storage specifiers in compound literals aren't yet implemented, and implementing them will require refactoring a lot of stuff
  • in general, constant expression evaluation with compound literals and struct/union types and all that is, kinda scuffed
  • the parser currently returns errors whenever the first parsing error is encountered; it has no way to recover. this should be changed to an interface more similar to that in c::check::. it should also be possible to emit warnings when parsing
  • c::lex::tokstr, c::lex::strerror, and c::parse::strerror use static buffers, which limits their usefulness. error messages constructed with an #error directive are also stored in a static buffer. especially if error handling in c::parse:: is refactored, these should be updated to use an interface similar to c::check::write_semantic. c::lex::tokstr should probably stay, but only as a convenience function which wraps around a more powerful function. importantly, it should be possible to have multiple parsers in use at once, without any issues, which it currently isn't
  • pretty much all of the builtin_* fields in c::check::context are unnecessary and wasteful
  • function prototype scope isn't correctly implemented. currently, in c::check::, function parameters aren't inserted into the scope until after the parameter list ends; for non-definitions they don't exist in any scope at all
  • universal character names are currently handled in c::lex::, which is almost always very convenient, except that the universal character name is lost when stringizing a token (with unary #). this is, annoyingly, nonconformant, so i've gotta figure something out here
  • the preprocessor code (mostly in c/parse/util.ha) is shit. this is the third time it's been rewritten, and it's still god awful. for one, it should be moved into its own file, separate from the utility functions, and it should be refactored to be actually fuckin readable. while at it, there should be a better more convenient way to treat keywords as identifiers, since currently the logic to do this is duplicated in a bunch of places (both within the preprocessor code, as well as in places like c/parse/attribute.ha. relatedly, __DATE__, __TIME__, and __TIMESTAMP__ should expand to string literals, so they don't return c::parse::builtin from c::parse::defined, and c::parse::builtin's docs should be updated to reflect that it's only special builtin macros like __FILE__ and __LINE__
  • some locations should be stored in the checked tree
  • lots of stuff in the AST should have a loc field, but currently doesn't, leading to less useful error messages in c::check::
  • it'd be cool if the error messages in c::check:: provided more context
  • there's a lot of spurious errors in c::check::, since builtin_invalid isn't yet implemented as a special thing (currently it's the same as builtin_void)
  • most attributes which i intend to support aren't yet implemented in c::check::. c::check:: also doesn't error when a recognized attribute is used in an incorrect location, like e.g. using [[fallthrough]] on a type specifier, or [[noreturn]] on a struct definition
  • crepl's codebase is mildly horrifying; it could use some cleaning up. i'm also almost positive that the garbage collector is completely broken, and probably fundamentally flawed in its design lol. i haven't really tested it at all
  • multicharacter constants should be supported
  • the parser should error out when a character constant's value overflows its type
  • the parser should error out when an integer constant's value isn't representable in any (non-bit-precise) integer type
  • the design of c::check::struct_type, c::check::union_type, and c::check::enum_type should be overhauled. specifically, every struct/union/enum should be like a c::check::tag_type, with an empty name if anonymous. this way, we can make anonymous structs and unions incompatible with all other structs and unions (unless typedef is used). this currently isn't implemented correctly
  • and much more!

#hi pls contribute

Send patches to ~sebsite/hare-c@lists.sr.ht (archive)

When debugging, hacc -dump is your friend. It dumps the checked tree without trying to generate any IR. c::check:: tests are located in cmd/hacc/+test/*.c (they need to be outside of c::check:: to avoid creating a circular dependency with c::parse::).

If you need help or have questions ping me on fedi or IRC or wherever :)

#Checklist

This checklist isn't exhaustive; I'm updating it as I go.

Legend:
 🗸  implemented
T   properly tested
X   currently can't be tested (e.g. runtime behavior)

   basics
T🗸   comments
T🗸     block
T🗸     line
T🗸   backslashes at end of line
     declarations
T🗸     must declare at least one identifier
       specifiers
         type specifiers
T🗸         signed, unsigned
T🗸           parse
T🗸         void
T🗸           parse
T🗸           incomplete type; cannot be completed
T🗸         char
T🗸           parse
T🗸         short
T🗸           parse
T🗸         int
T🗸           parse
T🗸         long
T🗸           parse
T🗸         float
T🗸           parse
T🗸         double
T🗸           parse
T🗸         long double
T🗸           parse
           struct/union
T🗸           struct
T🗸             declarations
T🗸               named
T🗸                 parse
T🗸                 check
T🗸               anonymous
T🗸                 parse
T🗸                 check
T🗸               definitions
T🗸                 parse
T🗸                 check
T🗸               forward
T🗸                 parse
T🗸                 check
 🗸           union
 🗸             declarations
 🗸               named
T🗸                 parse
 🗸                 check
 🗸               anonymous
T🗸                 parse
 🗸                 check
 🗸               definitions
T🗸                 parse
 🗸                 check
 🗸               forward
T🗸                 parse
 🗸                 check
             bit-fields
T🗸             parse
T🗸               named
T🗸               anonymous
               check
                 can't take address
                 width requirements
                   integer constant expression
                   non-negative
                     may be zero
                   less than or equal to width of type
                 width must be integer constant expression
                 width must be non-negative
                 may or may not have declarator if width > 0
                 zero width
                   declarator must be absent
                   causes following bit-fields to not be packed into the previous bit-field's unit
                 adjacent bit-fields are packed into the same union
                   true for both structs and unions
                 int is signed
T🗸           incomplete types disallowed
T🗸           must contain at least one named member
T🗸           variably modified member types disallowed
T🗸           tag inserted into scope immediately after it's declared
             tag is always declared when not used as type specifier
T🗸           when used as type specifier, tag is only declared if no other declaration is visible
T🗸           duplicate fields disallowed
T🗸             fields outside of nested structs/unions
T🗸             including fields from nested structs/unions
T🗸           fields may not have function type
             tag is inserted into scope immediately after declared
               definition for same struct/union can't appear within struct/union definition body
           enum
T🗸           declarations
T🗸             named
T🗸             anonymous
T🗸             definitions
T🗸             forward disallowed
T🗸           members inserted into scope
T🗸             parse
T🗸             no linkage
T🗸               can't be redeclared
T🗸             not lvalue
T🗸               can't take address
T🗸             inserted into scope immediately after identifier
T🗸           tag inserted into scope immediately after definition ends
T🗸           compatible type
T🗸             implicit enumerated type
T🗸               type is unsigned int if all members are representable
T🗸               otherwise, type is int
T🗸             enumeration member type is int
T🗸           enum type is compatible with, but not equivalent to, enumerated type
 🗸           error if any value would overflow enumeration member type
 🗸             with initializer
 🗸             without initializer
T🗸           duplicate fields disallowed
T🗸             enum member redeclares existing non-enum declaration
T🗸             enum member redeclares existing different enum declaration
T🗸             enum member redeclares member of same enum
T🗸           enum type is incomplete until definition ends
T🗸         typedefs
T🗸           parse
T🗸           inserted into scope
T🗸           file scope
T🗸             disallowed for variably modified types
T🗸             allowed otherwise
T🗸           block scope
T🗸             variably modified types allowed
T🗸             all other types allowed
T🗸       storage-class specifiers
T🗸         auto
T🗸           parse
T🗸         static
T🗸           parse
T🗸         register
T🗸           parse
T🗸           forbids taking address
T🗸             explicitly
T🗸             implicitly (e.g. array -> pointer conversion)
T🗸             also applies to derivative lvalues
T🗸         extern
T🗸           parse
T🗸           defaults to external linkage
T🗸           uses linkage of identifier visible in scope when it's internal or external
T🗸         typedef
T🗸           parse
T🗸           inserts type into scope
T🗸           can't have initializer
T🗸         at most one storage-class specifier per declaration (sans thread_local)
T🗸         duplicates disallowed
T🗸       duplicate specifiers
T🗸         disallowed for type specifiers
T🗸         allowed for other specifiers
T🗸       disallow incompatible
T🗸         type specifiers
T🗸         other specifiers
       qualifiers
T🗸       allowed in any order
         duplicates
T🗸         allowed
           warns
T🗸     variably modified declaration must have no linkage
       declarators
T🗸       pointer
         function
           parameters
T🗸           named
T🗸           anonymous
T🗸           arrays converted to (qualified) pointers
T🗸             still checked as arrays
T🗸           functions converted to function pointers
T🗸           may not declare more than one (non-tag) identifier
T🗸           may not be initialized
T🗸           without storage-class specifier
T🗸             defaults to implicit auto
T🗸           register storage-class specifier
T🗸             parse
T🗸             declares with register storage
T🗸             ignored for non-definitions
T🗸               may be different in compatible types
T🗸             applies even when other declarations' parameters have different/absent storage-class specifiers
T🗸           all other specifiers disallowed
T🗸           empty parameter list
T🗸             designates unspecified number of parameters
T🗸             isn't compatible with variadic function types
T🗸           treated as unqualified when taking composite type
             inserted into scope immediately after declared
               can be used in declarators of subsequent parameters
               can be used by previous parameters in k&r declarations
T🗸           void parameter
T🗸             (void) means no arguments
T🗸               must be unqualified, unnamed, and without register storage
T🗸               otherwise not treated specially
T🗸             warn when not special-cased
T🗸         return type requirements
T🗸           may not return array
T🗸           may not return function
T🗸         warn when function returning complete type doesn't have return statement
T🗸           except for main function in hosted environment
T🗸           all other functions
T🗸         storage-class specifier must be static or extern (explicit or otherwise)
T🗸         error out when explicitly given qualifiers (via typedef)
T🗸         implicit const qualifier
T🗸         qualifiers discarded for return type
T🗸       array
T🗸         without qualifiers
T🗸         qualifiers apply to element type
T🗸           don't apply to array itself before c23
T🗸         ...except as inner-most declarator of function parameter
T🗸           different from qualifying an array typedef or typeof
T🗸         qualifiers may only be in square brackets in inner-most declarator of function parameter
T🗸         if present, known size must be greater than zero
T🗸         element type must be complete object type
T🗸       identifier only
T🗸       parenthesized
T🗸       type names
T🗸         with declarator
T🗸         without declarator
T🗸     scopes
T🗸       file
T🗸         parse
T🗸         auto/register specifiers disallowed
T🗸         default storage
T🗸           extern for functions
T🗸           external linkage for objects
T🗸       block
T🗸         parse
T🗸         default storage
T🗸           static for functions (TODO: double check this)
T🗸           auto for objects
T🗸       duplicates
T🗸         file scope
T🗸           allowed for compatible declarations
T🗸             composite type
T🗸             compatible specifiers
T🗸           disallowed for incompatible declarations
T🗸           at most one definition allowed
T🗸         block scope
T🗸           disallowed within same scope
T🗸           shadowing in nested scopes
T🗸             shadowing block scoped declarations
T🗸             shadowing file scoped declarations
T🗸         struct/union/enum tags must always refer to same type
T🗸       unique namespaces
T🗸         identifiers
T🗸         struct/union/enum
T🗸         goto labels
T🗸           function scope
T🗸     declarations with incomplete type
T🗸       no linkage
T🗸         must be complete at time of declaration
T🗸       internally-linked
T🗸         must be complete by end of translation unit
T🗸         may be incomplete at time of declaration
T🗸       externally-linked
T🗸         tentative
T🗸           incomplete array
T🗸             completed with length 1
T🗸             warns
T🗸           everything else must be complete by end of translation unit
T🗸         extern
T🗸           all types allowed
T🗸       typedef
T🗸         all types allowed
T🗸     translation unit must not be empty (static assertions and empty declarations are ok)
       internally-linked declaration must be initialized if used
         except sizeof
         except alignof
         all other expressions
       warnings
         internally-linked declaration is never used
         internally-linked function is never defined
         initializer is implicitly truncated
     expressions
 🗸     constants and literals
T🗸       numbers
T🗸         preprocessing numbers
T🗸         integer constants
T🗸           bases
T🗸             decimal
T🗸             hex
T🗸             octal
T🗸           suffixes
T🗸             u
T🗸             l
T🗸           parse value
T🗸         floating constants
T🗸           decimal
T🗸           exponent
T🗸           suffixes
T🗸             f
T🗸             l
T🗸           parse value
         characters and strings
T🗸         character constants
T🗸           plain
T🗸           L prefix
 🗸         string literals
T🗸           plain
T🗸           L prefix
T🗸           string concatenation
T🗸             works for same-prefix strings
T🗸             disallowed for strings with different prefixes
T🗸           trigraph-like character sequences are escaped when unparsed (prior to c23)
 🗸           may be invalid utf-8
T🗸         escapes
T🗸           \a
T🗸           \b
T🗸           \f
T🗸           \n
T🗸           \r
T🗸           \t
T🗸           \v
T🗸           \x
T🗸             followed by one or more hexadecimal digits
T🗸           \NNN
T🗸             one to three octal digits
       sizeof
T🗸       parse
T🗸         type
T🗸         expression
T🗸           unambiguous expressions
T🗸           unparenthesized compound literal
X🗸       operand isn't evaluated if not variably modified
X🗸       operand is evaluated if variably modified
         operand must not be bit-field
T🗸       operand must be complete object
T🗸     array indexing
T🗸       parse
T🗸     struct/union field accessing
T🗸       forms (parse)
T🗸         a.b
T🗸         a->b
T🗸       check
T🗸     unary
T🗸       postfix
T🗸         parse
T🗸       prefix
T🗸         parse
T🗸         &
T🗸           compile-time
T🗸           runtime
T🗸           operand is lvalue
T🗸           operand is function designator
T🗸           operand is unary *
T🗸             allowed when explicit
T🗸             allowed when implicit from array indexing
X🗸             neither & nor * is evaluated
T🗸             constraints still apply
T🗸           everything else disallowed
T🗸       ++ and --
X🗸         equivalent to += 1
T🗸         ...except operand can't have a complex type
T🗸     binary
T🗸       parse
T🗸     assignment
T🗸       parse
T🗸       simple assignment
T🗸       compound assignment
X🗸         operands are only evaluated once
X🗸         computed value is computed equivalently to binary expression
T🗸         result of binary expression must be assignable to first operand's type
T🗸           second operand can't be a pointer for addition or subtraction
T🗸           everything else is fine
T🗸       first operand must be a mutable lvalue
T🗸       first operand must not be an array
T🗸     parenthesized
T🗸     ternary
T🗸     operator precedence
       conversion
T🗸       explicit (cast expressions)
T🗸         parse
T🗸         extends implicit type conversion rules
T🗸         integer -> pointer
T🗸         pointer -> integer
T🗸         pointer -> pointer
T🗸           object -> object
T🗸           function -> function
T🗸           no limitations for qualifiers
T🗸         any -> void
T🗸         everything else disallowed (except object <-> function; see extensions)
         implicit
           integer promotion
T🗸           non-bit-fields
             bit-fields
               width taken into account
               bit-precise integers don't change, regardless of width
T🗸         lvalue conversion
           types
T🗸           integer -> float
T🗸           float -> float
T🗸           integer -> integer
T🗸           float -> integer
T🗸           pointer -> bool
T🗸           array -> pointer
T🗸           function -> pointer
T🗸           pointer -> pointer
T🗸             pointer -> compatible pointer
T🗸             object pointer -> void pointer
T🗸             void pointer -> object pointer
T🗸             can't implicitly convert to any other pointer
T🗸             qualifiers may be added
T🗸             qualifiers may not be removed
             struct/union -> same struct/union
T🗸             tagged
               untagged
T🗸           integer -> pointer
T🗸             allowed when converting from integer constant 0
T🗸             disallowed otherwise
T🗸           nullptr -> pointer
T🗸           nullptr -> bool
T🗸           nullptr -> nullptr
T🗸           null pointer constant -> pointer
T🗸           null pointer constant -> nullptr
T🗸           everything else disallowed
T🗸     function call
T🗸       parse
T🗸       check
T🗸       arguments must have complete type
     constant expressions
T🗸     disallowed (sub-)expressions
T🗸       assignment
T🗸       increment/decrement
T🗸       array indexing
T🗸       arrow expression (->)
T🗸       unary *
T🗸         including as operand of unary &
T🗸       function call
T🗸       comma expression
T🗸       __builtin_va_arg
X🗸       __builtin_va_copy
X🗸       __builtin_va_end
X🗸       __builtin_va_start
       must evaluate to value which is representable in its type
       struct/union field access
 🗸       allowed if constexpr
         disallowed if not constexpr
         result is also constexpr
         result is named constant if struct/union object is named constant
 🗸     enum constant
T🗸     pre-defined constant
T🗸       true
T🗸       false
T🗸       nullptr
 🗸     constexpr object
       integer constant expressions
 🗸       top-most expression may be anything not generally disallowed, but see limitations
 🗸       result must be integer type
         limitations for operands
T🗸         integer constant
 🗸         named constant with integer type
           constexpr compound literal with integer type
             non-constexpr compound literal disallowed
T🗸         character constant
T🗸         translation-time sizeof
T🗸         alignof
T🗸         __builtin_offsetof
           cast
             additional expressions allowed as immediate operand
 🗸             floating-point constant
 🗸             named constant with arithmetic type
               constexpr compound literal with arithmetic type
                 non-constexpr compound literal disallowed
           everything else disallowed, unless not evaluated (sizeof/alignof)
             sizeof on variably modified type disallowed (since it's evaluated)
       arithmetic constant expressions
         TODO
T🗸     constant expression in initializer
T🗸     null pointer constant
T🗸       integer constant expression which evaluates to zero
T🗸       ...casted to void *
T🗸         unqualified
T🗸         qualified
T🗸         void can't be qualified
T🗸       doesn't otherwise affect type conversion rules
 🗸     integer promotion
 🗸       unsigned -> unsigned
 🗸       unsigned -> signed
 🗸       signed -> unsigned
 🗸       signed -> signed
 🗸       char's signedness is taken into account
T🗸     additional errors
T🗸       don't error when not evaluated
T🗸         including because of short-circuiting
T🗸         expression which isn't evaluated need not be constant
T🗸       division by zero
T🗸         integer division
T🗸         integer modulus division
T🗸         floating-point is ok
T🗸       bit-shift right operand exceeds integer width
T🗸       signed integer overflow
T🗸         addition
T🗸         subtraction
T🗸         multiplication
T🗸         division
T🗸         modulo
T🗸         left shift
T🗸         negation
 🗸     TODO: write more shit here i guess
     statements
T🗸     goto
T🗸       parse
T🗸       jump to any label within function
T🗸       disallow undefined labels
T🗸       may not jump from outside scope of VM type to inside scope
T🗸     compound
T🗸       parse
       labelled
         goto label
T🗸         parse
T🗸         unique per-function
           warn when unused
TT       case
T🗸         parse
T🗸         must be integer constant expression
T🗸         case type is converted to type of controlling expression
T🗸         warn if constant value is truncated
T🗸         must only appear in switch body
T🗸           direct descendant
T🗸           within another block
T🗸           disallowed everywhere else
T🗸           only visible to inner-most switch body
T🗸         each case in switch body is unique
T🗸         ...but nested switch statements may contain duplicates
T🗸       default
T🗸         parse
T🗸         must only appear in switch body
T🗸           direct descendant
T🗸           within another block
T🗸           disallowed everywhere else
T🗸           only visible to inner-most switch body
T🗸         at most one per switch body
T🗸         ...but nested switch statements may contain their own
T🗸       labelled statements may themselves be labelled
T🗸     empty
T🗸       parse
T🗸     if
T🗸       parse
T🗸       condition must be scalar
T🗸     while
T🗸       parse
T🗸       condition must be scalar
T🗸     do-while
T🗸       parse
T🗸       condition must be scalar
T🗸     for
T🗸       parse
T🗸       condition must be scalar (if present)
T🗸       initializer, condition, and afterthought may all be omitted
       switch
T🗸       parse
T🗸       value must be integer
T🗸       value is promoted
T🗸       no VM types not in scope of switch statement may be in scope of any cases
         warnings
T🗸         no cases in body
           contains unreachable code
T🗸     break
T🗸       parse
T🗸       allowed in loop
T🗸       allowed in switch statement
T🗸       disallowed everywhere else
X🗸       causes jump to outside whatever it applies to
T🗸         within check
X🗸         for-loop afterthought isn't evaluated
T🗸     continue
T🗸       parse
T🗸       allowed in loop
T🗸       disallowed everywhere else
T🗸       causes jump to end of loop it applies to
T🗸         within check
X🗸         so for-loop afterthought is evaluated, then condition
T🗸     return
T🗸       parse
T🗸       type of return value must be convertible to function return type
T🗸       function with void result type can't return an expression
T🗸         non-void expression
T🗸         void expression
T🗸       returning without expression from function with non-void result type
T🗸         warn before c99
T🗸         error after c99
T🗸       warn when used in noreturn function
T🗸     expression statements
T🗸       parse
T🗸       implicitly convert to void
T🗸     bindings
T🗸       TODO: write more shit here (stuff handled in fromabinding)
T🗸       enters scope before initializer is checked
 🗸   initializers
T🗸     parse
T🗸     non-compound
T🗸       without braces
T🗸       may be surrounded by braces
T🗸         when single scalar object
T🗸         when using string literal to initialize array
 🗸     compound initializers
T🗸       parse
T🗸       check
T🗸         array literals
T🗸           complete array type
T🗸           incomplete array type
T🗸             set length to length of literal
T🗸         struct literals
T🗸         union literals
T🗸       can be nested
 🗸       must always consist of constant expressions prior to c99
 🗸         braced scalar initializer need not be constant
T🗸     must be constant expression for objects with non-automatic storage duration
T🗸     may be any expression otherwise (except for compound initializer prior to c99)
T🗸     string literal may initialize array
T🗸       allowed when array length >= sizeof(string) - 1
T🗸       disallowed when array length < sizeof(string) - 1
T🗸       for incomplete array type, length defaults to sizeof(string)
X🗸       excess array elements are initialized to zero (nul)
T🗸   variadic functions
T🗸   function definitions
T🗸     declaration inserted into scope
T🗸       before body is parsed
T🗸       before body is checked
T🗸     declarator must be function
T🗸       non-pointer accepted
T🗸       pointer rejected
T🗸       typedef rejected
T🗸     without arguments (void)
T🗸     with named arguments
T🗸     argument types must be complete (sans (void) case)
T🗸     return type must be either complete type or void (in addition to other requirements)
T🗸   identifiers
T🗸   no such thing as an invalid token
T🗸   type size must be representable as size_t
T🗸     array
T🗸     struct
T🗸     union
     main function
 🗸     no requirements or restrictions imposed in freestanding environment
       hosted environment
 🗸       warn; don't error
         if __asm__("main") is given, no restrictions on the function's identifier are imposed
         TODO: main but with non-main __asm__?
T🗸       must be function
T🗸       must have strictly conforming prototype
T🗸         (void)
T🗸         (int, char **)
T🗸         ...or equivalent, expanding typedefs and performing usual parameter conversions
 🗸         all other forms rejected
 🗸       must have external linkage
T🗸       checked even if no definition is present
 🗸   environ declaration
 🗸     no restrictions imposed if not POSIX
 🗸     POSIX restrictions
T🗸       warn; don't error
 🗸       only apply restrictions when
 🗸         declaration has external linkage
T🗸         identifier is environ and __asm__ is absent
 🗸         ...or __asm__ is "environ"
 🗸         don't apply restrictions otherwise
 🗸       unqualified type must be compatible with char **
T🗸         no qualifiers present
 🗸         qualifiers present
 🗸         typedefs permitted
 🗸       by the end of translation, declaration should be extern
 🗸         can't have definition
   preprocessor
T🗸   macro definitions
T🗸     #define
T🗸       variable-like
T🗸         creates macro definition
T🗸       function-like
T🗸         creates macro definition
T🗸         duplicate parameters disallowed
T🗸       shadowing keywords
T🗸     undef
T🗸       shadowing keywords
T🗸       other identifiers
     macro substitution
T🗸     pre-defined macros
T🗸        standard
T🗸          constant
T🗸            __STDC__
T🗸            __STDC_HOSTED__
T🗸          special
T🗸            __FILE__
T🗸            __LINE__
X🗸            __DATE__
X🗸            __TIME__
T🗸        non-standard
T🗸          constant
T🗸            target-independent
T🗸              __HARE_C__
T🗸              __ORDER_LITTLE_ENDIAN__
T🗸              __ORDER_BIG_ENDIAN__
T🗸              __ORDER_PDP_ENDIAN__
T🗸              __STRICT_ANSI__
T🗸            target-dependent
T🗸              __BYTE_ORDER__
T🗸          special
T🗸            __BASE_FILE__
T🗸            __COUNTER__
T🗸            __INCLUDE_LEVEL__
T🗸            __FILE_NAME__
X🗸            __TIMESTAMP__
X🗸            __VERSION__
       operators
         # (%:)
 🗸         without whitespace
           whitespace is preserved, but collapsed into a single space
 🗸       ## (%:%:)
T🗸         with macro arguments
T🗸           only token closest to ## is concatenated
T🗸           when argument expands to no tokens, replace with placemarker
T🗸         with other tokens
 🗸         constructs new token
T🗸           non-preprocessing tokens
 🗸           constructed token won't be used for preprocessing (i.e. no # or ##)
 🗸     # has higher precedence than ##
T🗸     object-like
       function-like
T🗸       works when followed by left paren
T🗸       doesn't work when not followed by left paren
         parameters
T🗸         expand non-recursively
T🗸           expands macros
T🗸           everything else
T🗸         don't expand recursively
T🗸           from parameters
T🗸           from macros
           warn in c90 when argument expands to no tokens
T🗸     recursive
T🗸       macros being expanded can't be expanded again
T🗸         macro can't expand itself
T🗸         macro can't be expanded from within nested expansion
T🗸       everything else expands
     #include
T🗸     system headers
T🗸     non-system headers
T🗸       header exists
T🗸       header doesn't exist; fallback to system header
T🗸         header name doesn't have angle brackets
T🗸         header name has angle brackets
T🗸     macro expansion
T🗸       expands into system header
T🗸       expands into non-system header
T🗸       multiple macros may expand into one resource
T🗸     <this> is lexed as a system header string literal
T🗸       true in #include
T🗸       false everywhere else (prior to c23)
       warn if header name contains any of ', \, ", //, or /*
 🗸   conditional
T🗸     #if, #endif
T🗸     #else
T🗸     #elif
T🗸     defined
T🗸       non-parenthesized identifier
T🗸       paranthesized identifier
T🗸       error out when neither of the above forms is matched
T🗸     #ifdef
T🗸     #ifndef
 🗸     evaluation
T🗸       integer constant expressions
 🗸       other expressions disallowed
 🗸       keywords are treated as regular identifiers
T🗸       all arithmetic has intmax_t range
T🗸   #error
T🗸     errors out
X🗸     error message uses all tokens
X🗸     macros aren't expanded
     #warning
       actually warns
 🗸     warning message uses all tokens
 🗸     macros aren't expanded
 🗸     backported to all versions
T🗸   #line
T🗸     only change line number
T🗸     also change filename
T🗸     macros are expanded
T🗸     line number may not exceed 2147483647
   legacy (c90)
T🗸   trigraphs
     k&r-style functions
 🗸     empty parameter list
       non-empty parameter list without types
         TODO: allowed in c90 when k&r declarations aren't present?
       k&r-style parameter declarations
         parse
         only allowed in definitions
         every binding must correspond to a parameter
         allowed when parameter list doesn't have types
           empty parameter list
           non-empty parameter list without types
         disallowed when parameter list has types
           void
           more than zero parameters
         TODO: are duplicates allowed?
         declarations can reference parameters which come later (TODO: do they need to be declared first?)
         function type is unchanged (TODO: allowed when another declaration has types in parameter list?)
         parameter bindings are given the appropriate type within the function's scope
       warns
     implicit int
 🗸     implicit function declarations
 🗸       extern int ()
 🗸       happens when first operand of a call expression is an undeclared identifier
T🗸         unparenthesized
 🗸         can't be parenthesized
T🗸       warns
T🗸       inserted into current scope
       k&r parameter declarations may be omitted
         defaults to int
         warns
       type defaults to int when no type specifiers are present
         locations
 🗸         file scope
 🗸           declarations
 🗸             allowed with other specifiers or type qualifiers
T🗸             disallowed without other specifiers or type qualifiers
T🗸               including for non-definition function declarations
 🗸           function definitions
 🗸             allowed with other specifiers or type qualifiers
T🗸             allowed without other specifiers or type qualifiers
 🗸         block scope
 🗸           allowed with other specifiers or type qualifiers
 🗸           disallowed without other specifiers or type qualifiers
           k&r parameter
             allowed with other specifiers or type qualifiers
             disallowed without other specifiers or type qualifiers
 🗸         function parameter
 🗸           allowed with other specifiers or type qualifiers
 🗸           disallowed without other specifiers or type qualifiers
 🗸         struct/union field
 🗸           allowed with type qualifiers
 🗸           disallowed without type qualifiers
 🗸           without bit-field
 🗸           with bit-field
 🗸         type name
 🗸           allowed with type qualifiers
 🗸           disallowed without type qualifiers
T🗸       when ambiguous, identifier which is a typedef is treated as a type specifier
         warns
 🗸   implicit conversion
 🗸     pointer->integer
 🗸     integer->pointer
     pragma
T🗸     #pragma
T🗸     _Pragma
       STDC
T🗸       accept standardized
T🗸         FP_CONTRACT
T🗸         FENV_ACCESS
T🗸         CX_LIMITED_RANGE
X🗸       ignored (except for c23 additions)
T🗸       reject nonstandard
         valid locations
           file-scope, outside of declaration
 🗸           allowed
             error if contained within declaration, unless in block scope
             in effect until pragma state is changed or EOF
           beginning of compound statement, preceding all declarations and statements
 🗸           allowed
             multiple pragmas allowed; last one is used
             disallowed anywhere else in compound statement
             in effect for compound statement
             when compound statement ends, pragma state is restored to what it was prior to entering compound statement
           everywhere else disallowed
T🗸     implementation-defined
T🗸       accepted; ignored
 🗸     backported to c90
 🗸       standard pragmas still don't work until c99
     __has_include
       backported to all versions
T🗸 c95
T🗸   digraphs
T🗸   __STDC_VERSION__
   c99
T🗸   VLAs
T🗸     parse
T🗸     initialization
T🗸       can't be initialized by non-empty initializer
T🗸       can be initialized by empty initializer
T🗸     [*]
T🗸       TODO
     hex floating constants
T🗸     prefix / base
       parse value
T🗸     exponent
       warn if value can't be losslessly represented in its type
     universal character names
T🗸     in character constants and string literals
T🗸       parse
T🗸     in identifiers
T🗸       parse
T🗸       c99: conform to annex D (different from c11 annex D)
T🗸       c11, c17: conform to annex D (different from c99 annex D)
T🗸       c23: follow same rules as regular unicode codepoints in identifiers
T🗸     disallowed: (<0xa0 && !='$' && !='@' && !='`') || (>=0xd800 && <0xe000)
       preserved when stringizing
T🗸   initializer designators
T🗸     array
T🗸     struct
     declaration as for-loop initializer
T🗸     parse
T🗸     must be auto or register
T🗸       default is auto
       must not declare new tag
         tag may still be declared within expression
 🗸     enter scope immediately after declared
 🗸     allowed declaration kinds
 🗸       bindings
 🗸       static_assert
 🗸       attribute declaration
T🗸   specifiers
T🗸     type specifiers
T🗸       _Complex
T🗸       _Imaginary
T🗸         errors out
T🗸       long long
       inline
T🗸       parse
T🗸       may appear more than once
T🗸       can't be used outside of function declaration
T🗸       applies to function declaration
T🗸       if inline is used on any declaration, there must be a definition
 🗸       internally-linked
 🗸         can be used on any declaration; no change in behavior
         externally-linked; inline definitions
 🗸         function becomes inline definition if all file scope declarations use inline
 🗸         ...and none explicitly use extern
 🗸         otherwise no change in behavior; function isn't an inline definition
 🗸         inline definition doesn't provide external definition
           constraints
             may not define a modifiable object with static or thread duration
             may not use identifier with internal linkage
T🗸     static array qualifier
T🗸       allowed in function parameter
T🗸       disallowed everywhere else
T🗸       length must be provided
T🗸         fixed length
T🗸         vla
T🗸           specified size
T🗸           error when size is unspecified (`[static *]`)
T🗸   restrict
T🗸     parse
T🗸     restrictions on use (pun intended i think actually no it wasn't i lied)
T🗸       allowed on pointer to object type
T🗸       allowed on array of allowed type
T🗸       disallowed on non-array non-pointer types
T🗸       disallowed on pointer to function type
T🗸       disallowed on array of disallowed type
T🗸   variadic macros
T🗸     use of ... in definition
T🗸     __VA_ARGS__
T🗸       allowed in variadic macro definition
T🗸       disallowed everywhere else
T🗸     argument list must be preceded by comma
T🗸   intermixing declarations and statements in compound statements
T🗸   ll integer constant suffix
T🗸   __func__
T🗸     parse
T🗸     resolves to name of current function
T🗸     can't be redeclared at top-level
T🗸     has type 'const char []'
 🗸   implicit return 0 from main
T🗸     for hosted targets
 🗸     not for freestanding targets
     incomplete array struct member (flexible array member)
T🗸     allowed when it's the final member and other members are present
T🗸     disallowed otherwise
T🗸     not allowed in union
T🗸     struct type is still complete; size is as though incomplete array wasn't present
T🗸     but struct type can't be use in other structs or arrays
T🗸       including if it's (recursively) a member of a union
T🗸       including if it's an anonymous embedded struct
T🗸       other structs
       initialization in struct literal
 🗸       can be initialized when object has static storage duration
         can't be initialized otherwise
T🗸   pre-defined macros
T🗸     __STDC_ISO_10646__
     compound initializers/literals
T🗸     compound literals
T🗸       parse
T🗸       check
T🗸       type can't be VLA
T🗸     designators
T🗸       parse
T🗸         none
T🗸         array
T🗸         struct
T🗸       check
T🗸         none
T🗸         array
T🗸         struct
T🗸     multiple designators per object element may be given
T🗸     designators may be combined
       duplicate designators
 🗸       final object overwrites all previous objects
         warns
       warn when multiple designators for the same union are given
         excluding embedded structs
   c11
     specifiers
T🗸     _Thread_local
T🗸       parse
T🗸       may not appear alongside auto or register
T🗸       may not appear in block scope when no storage-class specifiers are present
T🗸       may not be used on function declaration
T🗸       must appear on all declarations of an object
       _Noreturn
T🗸       parse
T🗸       may appear more than once
T🗸       applies to function declaration
T🗸       can't be used outside of function declaration
         information persists in check
         warnings
T🗸         warn when return type isn't void
           warn when called in an expression which isn't a statement
       _Alignas
 🗸       expression
 🗸       type
         duplicates permitted; most strict alignment used
         invalid uses
           alongside typedef/register specifier
           on function
           on bit-field
         must not specify less strict alignment than default
         redeclarations
           first declaration has alignment specifier
             redeclarations may omit alignment specifier
             redeclarations with alignment specifier must specify same alignment
             error when redeclarations explicitly specify different alignment
               even if effective alignment would be the same
           first declaration doesn't have alignment specifier
             redeclarations must also not have alignment specifier
T🗸     _Atomic
T🗸       as specifier
T🗸       as qualifier
T🗸       specifier and qualifier can be used alongside each other
T🗸       errors out in check
T🗸   _Static_assert
T🗸     parse
T🗸       top-level
T🗸       within function
T🗸       within struct/union
T🗸     eval
T🗸       top-level
T🗸       within function
T🗸       within struct/union
T🗸       errors out when condition is false
T🗸   _Alignof
T🗸     parse
X🗸     operand isn't evaluated, even if variably modified
T🗸     operand must have defined alignment
T🗸     warn before c2y if operand is an incomplete array
 🗸   _Generic
T🗸     parse
T🗸     controlling expression conversions
T🗸       lvalue conversion
T🗸       array -> pointer
T🗸       function -> function pointer
T🗸     must match exactly one case
T🗸       at most one compatible case
T🗸       at most one default case allowed
T🗸       if no compatible case, default case must be present
T🗸     each non-default case must specify a non-variably-modified object type
 🗸     warnings
 🗸       no warnings when operand is type
T🗸       operand is expression
T🗸         case is qualified
T🗸         case is array
T🗸   prefixes
T🗸     character constants
T🗸       u8-
T🗸       u-
T🗸       U-
T🗸     string literals
T🗸       u-
T🗸       U-
T🗸   nested anonymous structs/unions
T🗸   pre-defined macros
T🗸     __STDC_IEC_559__
T🗸     __STDC_UTF_16__
T🗸     __STDC_UTF_32__
   c23
T🗸   unicode
T🗸     identifiers (without universal character names)
T🗸     universal character names must be less than U+110000
     specifiers
 🗸     _BitInt
T🗸       signed
T🗸         parse
T🗸       unsigned
T🗸         parse
T🗸       wb integer constant suffix
T🗸       operand must be integer constant expression
T🗸       signed width must be >= 2
T🗸       unsigned width must be >= 1
T🗸       doesn't undergo integer promotion
 🗸       has rank below equivalently sized basic integer type
 🗸       otherwise ranked based on width of both types
       typeof
T🗸       qualified (typeof)
T🗸       unqualified (typeof_unqual)
T🗸       with expression argument
T🗸       with type name argument
X🗸       operand isn't evaluated if not variably modified
         operand is evaluated if variably modified
         operand must not be bit-field
T🗸     decimal types
T🗸       _Decimal32
T🗸         type specifier
T🗸         df suffix
T🗸       _Decimal64
T🗸         type specifier
T🗸         dd suffix
T🗸       _Decimal128
T🗸         type specifier
T🗸         dl suffix
       constexpr
 🗸       parse
         check
 🗸         type
T🗸           implicitly qualified as const
T🗸           must be object (not function)
T🗸           must not be variably modified
 🗸           must not be volatile or restrict qualified
T🗸             constraint applies to type itself
 🗸             constraint applies recursively to subtypes
           initializer
T🗸           must be provided
T🗸           must be constant expression
             constraints
               value must be exactly representable in type
T🗸               integer
                 floating-point type
                   binary
                     real
                     complex
                   decimal
T🗸             pointer initializer must be null pointer
T🗸             integer initializer must be integer constant expression
 🗸             arithmetic initializer must be arithmetic constant expression
T🗸             real float initializer must have integer or real float type
T🗸             decimal float initializer must have decimal float type
               constraints apply recursively to subobjects
     preprocessor
       #embed
         system embeds
         non-system embeds
           embed exists
           embed doesn't exist; fallback to system header
             embed name doesn't have angle brackets
             embed name has angle brackets
         parameters
           standard
             if_empty
             limit
             prefix
             suffix
           vendor-specific
             gnu::offset
             warn for other non-standard parameters
           duplicates not permitted
           leading+trailing underscores permitted
 🗸       macro expansion
 🗸         expands into system embed
 🗸         expands into non-system embed
 🗸         multiple macros may expand into one resource
 🗸         only expand if the unexpanded tokens have a syntax error
 🗸       <this> is lexed as a system header string literal
         `defined` token prohibited
         warn if header name contains any of ', \, ", //, or /*
       __VA_OPT__
         allowed in variadic macro definition
T🗸       disallowed everywhere else
T🗸     comma before variadic macro arguments can be omitted when there's no argument list
T🗸     #elifdef, #elifndef
       standard pragmas
         FENV_ROUND
 🗸         value must be direction
           eval
           information persists in check
         FENV_DEC_ROUND
 🗸         value must be dec-direction
           eval
           information persists in check
T🗸   static_assert without reason
T🗸   type inference with auto
T🗸     parse
T🗸     infers type
T🗸     initializer must be provided
T🗸     compound initializer disallowed
T🗸     array/function initializer is converted to pointer
T🗸     declaration inserted into scope immediately after declared, but can't be used
T🗸     can't be used on function definition
T🗸     must have exactly one declarator
T🗸     must be direct declarator
T🗸     can't be used alongside other storage-class specifiers
T🗸     can be used alongside qualifiers
T🗸     can be followed by attributes
T🗸     only usable on bindings
T🗸       not function parameters
T🗸       not struct/union fields
T🗸       not type names
T🗸   nullptr
T🗸   labels
T🗸     labelled declarations
T🗸     at end of compound statement
T🗸     unchanged outside of compound statement
T🗸       outside of compound statement, only statements can be labelled
T🗸   binary integer constants
T🗸     parse value
T🗸   function declarations
T🗸     variadic function without parameters
T🗸     parameters in function definition need not be named
     attributes
       locations
T🗸       declaration
T🗸         top-level
T🗸           bindings
T🗸           function definitions
T🗸           as a declaration consisting of only attributes and nothing else
T🗸         within compound body
T🗸         for-loop initializer
T🗸         function parameter
T🗸         struct/union fields
T🗸         attribute declaration
T🗸           attribute list followed by semicolon, as though null statement
T🗸           only allowed where declarations are allowed
T🗸       binding
T🗸       base type
T🗸       declarator
T🗸         array
T🗸         function
T🗸         pointer
T🗸         with identifier
T🗸         without identifier
         struct/union/enum declaration
T🗸         parse
           allowed when defining struct/union/enum
           allowed in tag declaration where struct/union doesn't act as type specifier
             doesn't apply to enums since they can't be forward declared
           disallowed otherwise
T🗸       enum field
T🗸       statement
T🗸         non-null statements
T🗸         can't be on null statement (that makes it an attribute declaration)
T🗸       label
T🗸         on label in compound statement
T🗸         on labelled statement
       standard
         attributes
T🗸         [[noreturn]], [[_Noreturn]]
T🗸           disallow argument
T🗸           applicable to
T🗸             function declaration
T🗸             nothing else
T🗸           behaves equivalently to _Noreturn
           [[deprecated]]
             arguments
               may have optional argument
               must be unprefixed string literal (TODO?)
             applicable to
               struct/union declaration
               typedef declaration
               object declaration
               struct/union member
               function declaration
               enum
               enum member
               nothing else
             warn when name is used
           [[fallthrough]]
T🗸           disallow argument
             applicable to
T🗸             attribute declaration
               nothing else
T🗸           must only appear in switch body
T🗸           can't be last item in switch body
T🗸           next encountered item must be a case or default label
T🗸           next encountered item must appear within same switch body
T🗸           within iteration statement
T🗸             can't be last item
T🗸             next item must appear within same iteration statement
           [[maybe_unused]]
             disallow argument
             applicable to
               struct/union declaration
               typedef declaration
               object declaration
               struct/union member
               function declaration
               enum
               enum member
               label
               nothing else
           [[nodiscard]]
             arguments
               may have optional argument
               must be unprefixed string literal (TODO?)
             applicable to
               function declaration
               struct/union definition
               enum definition
               nothing else
             warn when value discarded
           [[reproducible]]
             disallow argument
             applicable to
               function declarator
               type specifier with function type
             warn when returns void
           [[unsequenced]]
             disallow argument
             applicable to
               function declarator
               type specifier with function type
             warn when returns void
T🗸       __has_c_attribute => 202311L
       non-standard
         warn
T🗸       __has_c_attribute => 0L
T🗸     leading+trailing underscores permitted
T🗸     keywords are treated as identifiers
T🗸     with prefix
T🗸     without prefix
T🗸     without arguments
T🗸     with arguments
T🗸       including balanced tokens
T🗸     multiple attribute lists are combined
T🗸     multiple attributes in [[brackets]]
T🗸   u8- string literals
T🗸   empty initializers
T🗸     parse
T🗸     scalar types
T🗸     array types
T🗸       allowed for complete array types
T🗸       disallowed for incomplete array types
T🗸         as initializer
T🗸         in compound literal
T🗸     struct types
T🗸     union types
T🗸   ' as separator
T🗸     integer constants
T🗸     floating constants
T🗸   empty declarations
     pre-defined macros
       keyword aliases
 🗸       alignas
T🗸       alignof
T🗸       bool
T🗸       true
T🗸         defined
T🗸         expands to _Bool value
T🗸       false
T🗸         defined
T🗸         expands to _Bool value
T🗸       static_assert
T🗸       thread_local
         when equivalent keyword is re-defined, alias macro doesn't expand said definition (TODO: maybe just don't bother with this?)
T🗸     __has_c_attribute
       __has_embed
T🗸     constants
T🗸       __STDC_EMBED_NOT_FOUND__
T🗸       __STDC_EMBED_FOUND__
T🗸       __STDC_EMBED_EMPTY__
     enum
       explicit enumerated type
T🗸       parse
         check
T🗸         explicitly specified type is used as enumerated type
T🗸         type constraints
T🗸           must be integer type
T🗸           can't be bit-precise
T🗸           can't be enum
T🗸         qualifiers are discarded
T🗸         enum type is completed immediately after explicitly specified type is parsed
T🗸         forward declarations
T🗸           allowed when not used as type specifier (i.e. nothing else is declared)
T🗸           disallowed when used as type specifier
           multiple declarations of the same enum tag
             explicit enumerated type must be provided by either all declarations or none
               explicit enumerated type need not be (and can't be) provided when enum without definition is used as type specifier
T🗸           the enumerated types must all be compatible
T🗸         error if value would be implicitly truncated
T🗸           with initializer
T🗸           without initializer
T🗸     other type additions/changes
T🗸       implicit enumerated type
T🗸         order is (un)signed int, (un)signed long, (un)signed long long
T🗸       enumeration member type
T🗸         with implicit enumerated type
T🗸           within enum definition (each individual member)
T🗸             type is int if representable
T🗸             otherwise, if there's an initializer, type is that of the initializer
T🗸             otherwise, type is basic integer type with the same signedness as the previous member, whose rank is the minimum which isn't smaller than that of the previous member but which can represent the new implicit value
T🗸               error if no suitable type exists
T🗸           after enum is defined (all members collectively)
T🗸             type is int if all representable
T🗸             otherwise, type is the same as the enumerated type
T🗸         with explicit enumerated type
T🗸           type is the same as the enumerated type
T🗸             within enum definition
T🗸             after enum is defined
T🗸   storage-class specifiers in compound literals
T🗸     allowed
T🗸     disallowed for cast expressions
T🗸   treat empty parameter list as identical to (void)
 🗸 c2y
 🗸   unary increment/decrement operand can have complex type
 🗸   _Generic
 🗸     with type operand
T🗸       parse
 🗸       doesn't undergo lvalue or pointer conversion
 🗸       cases can be function types
 🗸       cases can be incomplete
 🗸     cases can be variably modified
 🗸       with known length
 🗸       [*] allowed
 🗸   pre-defined macros
 🗸     __STDC_IEC_60559_COMPLEX__
T🗸   _Imaginary is no longer a keyword
T🗸   0o integer constant prefix
T🗸   escapes
T🗸     \x{...}
T🗸       any number of hexadecimal digits in braces
T🗸     \o{...}
T🗸       any number of octal digits on braces
T🗸     \u{...} (universal character names)
T🗸       in character constants and string literals
T🗸       in identifiers
 🗸   _Lengthof
 🗸     parse
 🗸       type and expression, exactly as with sizeof
 🗸     operand must be an array
 🗸       fixed length
 🗸       vla
 🗸     operand must be complete
 🗸     operand isn't evaluated if it isn't a vla
 🗸       even if it's variably modified
 🗸         even for multidimensional arrays where inner arrays are vlas
 🗸     operand is evaluated if it's a vla
     case ranges
 🗸     parse
 🗸       allowed in case
 🗸       can't be parenthesized
 🗸       disallowed everywhere else
       both operands must be integer constant expressions
       types are converted to type of controlling expression
       conversion can't change value (i.e. implicit truncation is an error)
       low > high
         warn
         case is never reached
         no effect on checks for duplicate cases
       duplicate cases disallowed
         as though each case in range were listed separately
     labelled statements
 🗸     control flow
 🗸       break with label
 🗸         parse
X🗸         breaks out of named statement
 🗸         label must apply to loop or switch statement
 🗸       continue with label
 🗸         parse
X🗸         continues named loop
 🗸         label must apply to loop
 🗸     targets
 🗸       loops
 🗸       switch statements
       target must be a statement which the break/continue is within
 🗸     labels can still be target of goto
 🗸     statement can have multiple names (labelled more than once)
 🗸     works with label items in compound statement
 🗸     works with labelled statement
 🗸     label doesn't apply to statement following a null statement
   extensions
     preprocessor
       redefinition of macros
T🗸       user-defined
T🗸       pre-defined
         warns when new definition isn't identical to old
           doesn't warn when new definition is identical
X🗸     __DATE__ and __TIME__ use SOURCE_DATE_EPOCH if set
T🗸     __has_attribute
       linemarkers
     builtin identifiers
T🗸     can't be redeclared at top-level
       __builtin_offsetof
T🗸       parse
T🗸         second operand is identifier
T🗸         designator in second operand
T🗸           array designations
T🗸           struct designations
         check
           can't be used on bit-fields
T🗸         can be used on non-bit-fields
T🗸         second operand is identifier
T🗸         designator in second operand
T🗸           array designations
T🗸             work with array types
T🗸               with known size
T🗸               incomplete
T🗸             error when field isn't an array
T🗸             error when out of bounds for known size
T🗸           struct designations
T🗸             work with struct and union types
T🗸             error when field isn't a struct or union
 🗸         allowed types
T🗸           struct
 🗸           union
 🗸             no diagnostic, despite technically being an extenion
 🗸           everything else disallowed
           error when type is defined within type operand
       __builtin_pp_embed
T🗸       parse
         check
         warnings and errors
           error when type isn't a character type
       __builtin_unreachable
T🗸       parse
T🗸       persists in check
         warnings and errors
           warn when used as an expression
T🗸     __builtin_va_arg
T🗸       parse
T🗸       check
T🗸       warnings and errors
T🗸         when type name can't be made a pointer by postfixing it with *
T🗸           warn before c23
T🗸           no diagnostic since c23
T🗸         error when type name is an array type or function type
T🗸         error when type name is an incomplete type
T🗸         error when first argument isn't a va_list
T🗸     __builtin_va_copy
T🗸       parse
T🗸       check
T🗸       warnings and errors
T🗸         error when arguments aren't both va_lists
T🗸     __builtin_va_end
T🗸       parse
T🗸       check
T🗸       warnings and errors
T🗸         error when argument isn't a va_list
T🗸     __builtin_va_list
T🗸       parse
T🗸       check
       __builtin_va_start
 🗸       parse
 🗸         second argument must be identifier before c23
 🗸         second argument must be identifier or integer constant 0 after c23
T🗸       check
         warnings and errors
T🗸         error when function isn't variadic
T🗸         last named parameter has register storage-class specifier
T🗸           warn before c23
T🗸           no diagnostic since c23
           TODO: last named parameter is declared as array type or function type?
T🗸         last named parameter's type is incompatible with type after default argument promotion
T🗸           disallowed types
T🗸             integer type ranked lower than int
T🗸             float
T🗸           warn before c23
T🗸           no diagnostic since c23
           optional argument isn't the last named function parameter
             error before c23
             warn since c23
     __asm__
       declarations
T🗸       parse
T🗸         disallowed on function definition
T🗸         disallowed for struct/union fields
T🗸         no other restrictions
T🗸         string literal must be unprefixed
T🗸         empty string unparses correctly
T🗸         trigraph-like character sequences are escaped when unparsed (prior to c23)
         check
T🗸         allowed declarations
T🗸           static or thread storage duration
T🗸             file scope
T🗸             block scope
T🗸           register storage-class
T🗸           disallowed otherwise
T🗸         empty symbol disallowed
T🗸         no other restrictions imposed on symbol
T🗸         duplicate declarations must agree on symbol (if present)
           TODO: what's the deal with main?
       statements
         basic
           outside function
 🗸           parse
             check
           inside function
 🗸           parse
             check
 🗸         string is interpreted literally, without % escapes
 🗸           don't check as format string
         extended
 🗸         parse
           check
 🗸         only allowed inside function
 🗸         double colons are separated into two single colons
 🗸         qualifiers
 🗸           goto
 🗸           inline (or __inline__)
 🗸           volatile
           % escapes in format string are checked
             %n (n = number)
               operands are numbered starting at zero
                 operands with symbolic names are also numbered
               error when no operand corresponds to the given number
             %[symbolic_name]
             %=
               replaced with number unique to __asm__ statement
             multiple assembler dialects
               {foo|bar|baz}
               may supply only one dialect
               can't be nested
               escapes within braces are recognized
             escape special characters
               %%
               %{
               %|
               %}
 🗸         output operands
 🗸           parse
 🗸           expression must be a mutable lvalue
 🗸         input operands
 🗸           parse
 🗸         clobbers
 🗸           parse
 🗸         goto labels
 🗸           parse
 🗸           all identifiers must be valid goto labels in current function scope
 🗸           at least one label must be provided
 🗸           labels can't be provided if goto qualifier isn't used
 🗸           labels must be provided if goto qualifier is used
 🗸         no two operands can share a symbolic name
           check constraint syntax
T🗸   casting between object pointer and function pointer
T🗸     errors when POSIX_C isn't used
T🗸     no diagnostic otherwise
X🗸       does the Right Thing
     __attribute__
 🗸     locations
 🗸       within specifier/qualifier list
 🗸         declaration
T🗸           parse
T🗸             binding
T🗸               file scope
T🗸               block scope
T🗸             function parameter
T🗸             struct/union field
T🗸             disallowed for static_assert
T🗸               file scope
T🗸               block scope
T🗸             allowed for [[attribute]] declaration
T🗸               file scope
T🗸               block scope
 🗸           always applies only here when ambiguous
T🗸           disallowed without other specifiers
T🗸             even prior to c99
T🗸         type name
T🗸           parse
T🗸           without other specifiers
T🗸             prior to c99
T🗸               allowed outside of function parameters
T🗸               disallowed for function parameters
T🗸             disallowed since c99
 🗸       binding
 🗸         before declarator
T🗸           disallowed on first declarator
 🗸           allowed for subsequent declarators
T🗸         after declarator
T🗸           parse
T🗸           must go after __asm__ if present
T🗸           disallowed in function definition
T🗸       struct/union/enum
T🗸         parse
T🗸           after struct/union/enum token
T🗸           after closing brace
T🗸             always applies only here when ambiguous
T🗸       enum field
T🗸       null statement
T🗸         parse
T🗸         only if not labelled
T🗸         must precede any standard attributes
T🗸         standard attributes only allowed where declaration would be allowed
T🗸       label
T🗸         parse
T🗸         disallowed with case and default
T🗸         always applies only here when ambiguous
T🗸       declarator
T🗸         locations within declarator
T🗸           after *
T🗸           parenthesized, before nested declarator
T🗸             parse
T🗸             `(__attribute__` always begins function declarator, not nested declarator
T🗸               even if it would parse if interpreted as nested declarator, still error out
T🗸           alongside qualifiers within array brackets
T🗸         application
T🗸           to declarator
T🗸           to base type
T🗸         parse
       warn for unrecognized attributes
       supported attributes
T🗸       noreturn, _Noreturn
T🗸         behaves equivalently to [[noreturn]]
         alias
         aligned
         always_inline
           error when argument used
           applicable to
             function declaration
             nothing else
           must be used alongside inline specifier
           information persists in check
         const
           behaves equivalently to [[unsequenced]]
           ...but also applicable to function declaration
         deprecated
           behaves equivalently to [[deprecated]]
 🗸       fallthrough
 🗸         behaves equivalently to [[fallthrough]]
 🗸           applicable to null statement
         format
           arguments
             must be exactly three
             archetype
               must be unprefixed string literal
               must be one of printf, scanf, strftime, or strfmon
             string-index
               must be integer constant (TODO?)
               must refer to `const char *` parameter (1-indexed) (TODO: unqualified `char *`?)
             first-to-check
               must be integer constant (TODO?)
               allowed values
                 index of valid (possibly variadic) parameter (1-indexed)
                 0
                   arguments aren't available to check
                 other values disallowed
           warnings
             invalid format string
               printf
               scanf
               strftime
               strfmon
             invalid arguments
         gnu_inline
           error when argument used
           applicable to
             function declaration
             nothing else
           must be used alongside inline specifier
           must appear in all declarations of same function
           causes behavior for no storage-class specifier / extern to be flipped
             INLINE if extern everywhere, otherwise STATIC_EXTERNAL_DEFN
         noinline
           error when argument used
           applicable to
             function declaration
             nothing else
           error when used with inline specifier
             in same declaration
             in other redeclaration
           information persists in check
         nonnull
         packed
           error when argument used
           applicable to
T🗸           struct
             struct field
             union
             union field
             nothing else
T🗸         check
T🗸           TODO: write shit here
         pure
           behaves equivalently to [[reproducible]]
           ...but also applicable to function declaration
         returns_nonnull
           error when argument used
           applicable to
             function declaration
             nothing else
           function must return pointer type
           warn if return statement in function returns null pointer constant or constant expression with value null
         section
T🗸         arguments
T🗸           must be exactly one
T🗸           must be unprefixed string literal
T🗸           must be nonempty
           applicable to
T🗸           function declaration
T🗸           object declaration
T🗸             with static or thread storage duration
T🗸               file scope
T🗸               block scope
T🗸             disallowed for automatic storage duration
             nothing else
           duplicates must agree on section (if present)
             in same declaration
               in same location
               in different locations (e.g. declaration and binding)
T🗸           in separate declarations
           TODO: what's the deal with main?
         unavailable
           arguments
             may have optional argument
             must be unprefixed string literal
           applicable to
             declaration
             nothing else
           error whenever the declaration is used
             don't error when redeclared
           don't emit(?)
         unused
           behaves equivalently to [[maybe_unused]]
         used
           error when argument used
           applicable to
             declaration
             nothing else
           persists in check
           silences unused declaration warning
         visibility
         weak
T🗸     attributes may also be used as c23 attributes with gnu:: prefix
T🗸     leading+trailing underscores permitted
     __inline__
T🗸     equivalent to inline since c99
       implicit [[gnu::gnu_inline]] prior to c99
T🗸   __typeof__
T🗸     equivalent to typeof, except allowed in all versions
T🗸   __typeof_unqual__
T🗸     equivalent to typeof_unqual, except allowed in all versions
T🗸   imaginary constants
T🗸     have complex type
T🗸     suffixes
T🗸       i
T🗸       j
T🗸     supported after c99
T🗸     not supported before c99
   additional warnings
     SOURCE_DATE_EPOCH is invalid
     unused internally-linked objects and functions
     statement follows statement which manipulates control flow or won't return
       statement being followed
T🗸       break
T🗸       continue
T🗸       goto
T🗸       return
         noreturn function call
T🗸       __builtin_unreachable()
T🗸       potentially labelled
T🗸       compound whose last non-unlabelled-null statement is any of these
T🗸       nothing else
       following statement
T🗸       warn if
T🗸         statement which isn't included in "don't warn if" below
T🗸         bindings or tag declaration
           fallthrough attribute declaration
T🗸           [[fallthrough]]
             __attribute__((fallthrough))
         don't warn if
T🗸         null statement
T🗸           warn for following statements which fit the requirements
T🗸         labelled statement
T🗸           don't warn for following statements
T🗸         __builtin_unreachable() expression
T🗸           continue to warn for following statements which fit the requirements
T🗸         static assertion
T🗸           warn for following statements which fit the requirements
           nonstandard (non-fallthrough) attribute declaration
T🗸           standard attributes
             gnu-style attributes
T🗸           don't warn for following statements