chiark / gitweb /
Merge branches 'mdw/kwargs' and 'mdw/c11'
authorMark Wooding <mdw@distorted.org.uk>
Thu, 26 May 2016 08:26:09 +0000 (09:26 +0100)
committerMark Wooding <mdw@distorted.org.uk>
Sun, 29 May 2016 14:33:27 +0000 (15:33 +0100)
* mdw/kwargs: (29 commits)
  New feature: initialization keyword arguments.
  src/method-{proto,impl}.lisp: New `method-keyword-argument-lists' protocol.
  New feature: proper object lifecycle protocol; init and teardown fragments.
  src/builtin.lisp: Bind `me' around slot initializers, and define the order.
  Replace the `init' class-slot function with an `init' message.
  Compatibility: the `init' function no longer calls `imprint' for you.
  src/method-{proto,impl}.lisp: Introduce `effective-method-live-p' protocol.
  doc/runtime.tex, lib/sod.3: Restructure the runtime library reference.
  doc/concepts.tex: Reorganize the instance lifecycle material.
  doc/: Miscellaneous clarifications and rewordings.
  doc/concepts.tex: Don't highlight `primary' as literal, because it's not.
  src/: Abolish the distinction between different kinds of initializers.
  src/method-{proto,impl}.lisp: Abolish `sod-message-no-varargs-tail'.
  New feature: messages with keyword arguments!
  lib/: Pure C machinery for handling `keyword arguments' to functions.
  doc/runtime.tex: Demote the object-system support stuff to a section.
  src/c-types-*.lisp: New type for functions which take keyword arguments.
  src/c-types-parse.lisp (parse-declarator): Refactor argument list parsing.
  src/c-types-parse.lisp (parse-declarator): Explain how it works.
  src/: New function `reify-variable-argument-tail'.
  ...

* mdw/c11:
  src/c-types-impl.lisp, src/c-types-parse.lisp: Support C11 `_Alignas'.
  src/c-types-{proto,impl,parse}.lisp: Add `storage specifiers' to the model.
  src/c-types-{impl,parse}.lisp: Support C11 `_Atomic'.
  src/c-types-{impl,parse}.tex: Support `_Atomic' types.
  src/c-types-parse.lisp: Introduce a pluggable parser for declspecs.
  src/c-types-parse.lisp: Hoist up the `ds-FOO' methods for raw types.
  src/c-types-parse.lisp: Improve handling of compatibility keywords.
  src/final.lisp: Add function for interactively testing type parsing.
  src/c-types-proto.lisp, src/c-types-impl.lisp: Qualifier name protocol.
  src/module-parse.lisp (parse-raw-class-item): Commit after declarator.
  Add a new class slot `align', holding the instance layout alignment.
  src/c-types-impl.lisp (make-or-intern-c-type): Pull out useful function.
  src/c-types-parse.lisp, src/c-types-proto.lisp: Some minor cleanups.
  src/parser/parser-proto.lisp: Add functions returning standard parsers.
  src/parser/: Allow parsers to commit to a parse while peeking.
  doc/clang.tex: Improve documentation of C type spec expansion.

Conflicts:
doc/SYMBOLS (regenerated)
doc/syntax.tex (hand-merge; extra parameter to <declarator>)
lib/sod.h (simple hand-merge)
src/sod-module.5 (extra parameter to <declarator>, added by hand)

12 files changed:
1  2 
doc/SYMBOLS
doc/clang.tex
doc/structures.tex
doc/syntax.tex
lib/sod-structs.3
lib/sod.h
src/builtin.lisp
src/c-types-impl.lisp
src/c-types-parse.lisp
src/c-types-proto.lisp
src/module-parse.lisp
src/sod-module.5

diff --combined doc/SYMBOLS
index 00bfb3b7ae0fdb6acaf8d549efe58df807a81fba,20e2eef1918dc97e4d4dc9e6b7c0c347badc514b..cec9fa3023e8782bb238ed69a510df5101d9e8c2
@@@ -16,15 -16,17 +16,19 @@@ c-types-class-impl.lis
  
  c-types-impl.lisp
    cl:*                                          variable function c-type
+   alignas
+   alignas-storage-specifier                     class
    cl:array                                      class c-type
+   atomic                                        c-type
    bool                                          c-type
    c-array-dimensions                            generic
    c-array-type                                  class
+   c-atomic-type                                 class
    c-enum-type                                   class
    c-function-arguments                          generic
 +  c-function-keywords                           generic
    c-function-type                               class
 +  c-keyword-function-type                       class
    c-pointer-type                                class
    c-struct-type                                 class
    c-tagged-type-kind                            generic
    long-long                                     c-type
    long-long-int                                 c-type
    make-array-type                               function
+   make-atomic-type                              function
    make-c-tagged-type                            function
    make-enum-type                                function
    make-function-type                            function
 +  make-keyword-function-type                    function
    make-pointer-type                             function
    make-simple-type                              function
    make-struct-type                              function
    make-union-type                               function
 +  merge-keyword-lists                           function
    cl:nil                                        constant c-type parser
    pointer                                       c-type
 +  pprint-argument-list                          function
 +  pprint-c-function-type                        function
    ptr                                           c-type
    ptrdiff-t                                     c-type
 +  reify-variable-argument-tail                  function
    cl:schar                                      function setf c-type
    short                                         c-type
    short-int                                     c-type
    size-t                                        c-type
    sllong                                        c-type
    slong                                         c-type
+   specs                                         c-type
    sshort                                        c-type
    cl:string                                     function class c-type opthandler
    struct                                        c-type
@@@ -148,30 -147,39 +154,40 @@@ c-types-parse.lis
  
  c-types-proto.lisp
    argument                                      class
 +  argument-default                              function
    argument-name                                 function
    argument-type                                 function
    argumentp                                     function
    c-name-case                                   function
+   c-qualifier-keyword                           generic
+   c-storage-specifiers-type                     class
    c-type                                        macro class
    c-type-alias                                  macro
    c-type-equal-p                                generic
+   c-type-qualifier-keywords                     function
    c-type-qualifiers                             generic
    c-type-space                                  function
+   c-type-specifiers                             generic
    c-type-subtype                                generic
    canonify-qualifiers                           function
    commentify-argument-name                      generic
    defctype                                      macro
+   define-c-storage-specifier-syntax             macro
    define-c-type-syntax                          macro
+   expand-c-storage-specifier                    generic
+   expand-c-storage-specifier-form               generic
    expand-c-type-form                            generic
    expand-c-type-spec                            generic
    format-qualifiers                             function
    make-argument                                 function
    maybe-in-parens                               macro
+   pprint-c-storage-specifier                    generic
    pprint-c-type                                 generic
+   print-c-storage-specifier                     generic
    print-c-type                                  generic
    qualifiable-c-type                            class
    qualify-c-type                                generic
+   wrap-c-type                                   function
  
  class-finalize-impl.lisp
    c3-cpl                                        function
@@@ -219,7 -227,6 +235,7 @@@ class-layout-proto.lis
    effective-slot-class                          generic
    effective-slot-direct-slot                    generic
    effective-slot-initializer                    generic
 +  find-slot-initargs                            generic
    find-slot-initializer                         generic
    ichain                                        class
    ichain-body                                   generic
    vtmsgs-entries                                generic
    vtmsgs-subclass                               generic
  
 +class-make-impl.lisp
 +  check-method-argument-lists                   function
 +  check-method-return-type                      function
 +  check-method-return-type-against-message      function
 +
  class-make-proto.lisp
    check-message-type                            generic
    check-method-type                             generic
    make-sod-class                                function
 +  make-sod-class-initfrag                       generic
    make-sod-class-initializer                    generic
 +  make-sod-class-tearfrag                       generic
    make-sod-initializer-using-slot               generic
    make-sod-instance-initializer                 generic
    make-sod-message                              generic
    make-sod-method                               generic
    make-sod-method-using-message                 generic
    make-sod-slot                                 generic
 +  make-sod-slot-initarg                         generic
 +  make-sod-slot-initarg-using-slot              generic
 +  make-sod-user-initarg                         generic
 +  sod-initarg-argument                          generic
    sod-message-method-class                      generic
  
  class-output.lisp
@@@ -307,7 -303,6 +323,7 @@@ classes.lis
    sod-class-class-initializers                  generic setf
    sod-class-direct-superclasses                 generic
    sod-class-ilayout                             generic
 +  sod-class-initfrags                           generic setf
    sod-class-initializer                         class
    sod-class-instance-initializers               generic setf
    sod-class-messages                            generic setf
    sod-class-precedence-list                     generic
    sod-class-slots                               generic setf
    sod-class-state                               generic
 +  sod-class-tearfrags                           generic setf
    sod-class-type                                generic
    sod-class-vtables                             generic
 +  sod-initarg                                   class
    sod-initializer                               class
    sod-initializer-class                         generic
    sod-initializer-slot                          generic
 -  sod-initializer-value-form                    generic
 -  sod-initializer-value-kind                    generic
 +  sod-initializer-value                         generic
    sod-instance-initializer                      class
    sod-message                                   class
    sod-message-class                             generic
    sod-method-type                               generic
    sod-slot                                      class
    sod-slot-class                                generic
 +  sod-slot-initarg                              class
    sod-slot-name                                 generic
    sod-slot-type                                 generic
 +  sod-user-initarg                              class
  
  codegen-impl.lisp
    codegen                                       class
@@@ -353,7 -345,6 +369,7 @@@ codegen-proto.lis
    *null-pointer*                                variable
    *sod-ap*                                      variable
    *sod-master-ap*                               variable
 +  banner-inst                                   class
    block-inst                                    class
    break-inst                                    class
    call-inst                                     class
    codegen-pop-block                             generic
    codegen-pop-function                          generic
    codegen-push                                  generic
 +  cond-inst                                     class
    continue-inst                                 class
    convert-stmts                                 function
    definst                                       macro
    deliver-call                                  function
    deliver-expr                                  function
    do-while-inst                                 class
 +  emit-banner                                   function
    emit-decl                                     generic
    emit-decls                                    generic
    emit-inst                                     generic
    emit-insts                                    generic
    ensure-var                                    generic
    expr-inst                                     class
 +  for-inst                                      class
 +  format-banner-comment                         function
    format-compound-statement                     macro
    format-temporary-name                         generic
    function-inst                                 class
    inst                                          class
    inst-alt                                      generic
    inst-args                                     generic
 +  inst-banner                                   generic
 +  inst-banner-args                              generic
    inst-body                                     generic
    inst-cond                                     generic
    inst-conseq                                   generic
 +  inst-control                                  generic
    inst-decls                                    generic
    inst-expr                                     generic
    inst-func                                     generic
    inst-name                                     generic
    inst-op                                       generic
    inst-type                                     generic
 +  inst-update                                   generic
    inst-var                                      generic
 +  make-banner-inst                              function
    make-block-inst                               function
    make-break-inst                               function
    make-call-inst                                function
 +  make-cond-inst                                function
    make-continue-inst                            function
    make-do-while-inst                            function
    make-expr-inst                                function
 +  make-for-inst                                 function
    make-function-inst                            function
    make-if-inst                                  function
    make-return-inst                              function
@@@ -435,6 -415,7 +451,7 @@@ final.lis
    *sod-version*                                 variable
    exercise                                      function
    test-module                                   function
+   test-parse-c-type                             function
    test-parser                                   macro
  
  fragment-parse.lisp
@@@ -490,8 -471,6 +507,8 @@@ method-proto.lis
    effective-method-basic-argument-names         generic
    effective-method-class                        generic
    effective-method-function-name                generic
 +  effective-method-keywords                     generic
 +  effective-method-live-p                       generic
    effective-method-message                      generic
    ensure-ilayout-var                            function
    inst-chain-head                               generic
    inst-expr                                     generic
    invoke-delegation-chain                       function
    invoke-method                                 function
 +  keyword-message-p                             function
    make-convert-to-ilayout-inst                  function
    make-method-entries                           generic
    make-trampoline                               function
    method-entry-function-name                    generic
    method-entry-function-type                    generic
    method-entry-slot-name                        generic
 +  method-keyword-argument-lists                 generic
    primary-method-class                          generic
    simple-method-body                            generic
    sod-message-argument-tail                     generic
    sod-message-effective-method-class            generic
 -  sod-message-no-varargs-tail                   generic
    sod-method-function-name                      generic
    sod-method-function-type                      generic
    sod-method-next-method-type                   generic
@@@ -609,13 -587,11 +626,14 @@@ pset-proto.lis
    store-property                                function
    with-pset-iterator                            macro
  
 +Leaked slot names: cl:type
 +  sod-initarg: cl:type
 +
  Classes:
  cl:t
    sb-pcl::slot-object
      cl:standard-object
+       alignas-storage-specifier
        base-offset
        sod::basic-codegen
          codegen
        c-type
          c-array-type
          c-function-type
 +          c-keyword-function-type
+         c-storage-specifiers-type
          qualifiable-c-type
+           c-atomic-type
            c-pointer-type
            simple-c-type
              c-class-type
        ichain
        ilayout
        inst
 +        banner-inst
          block-inst
          break-inst
          call-inst
 +        cond-inst
          continue-inst
          convert-to-ilayout-inst
          do-while-inst
          expr-inst
 +        for-inst
          function-inst
          if-inst
          return-inst
            cl:class [sb-pcl::definition-source-mixin sb-pcl::standard-specializer]
        sequencer
        sod-class
 +      sod-initarg
 +        sod-slot-initarg
 +        sod-user-initarg
        sod-initializer
          sod-class-initializer
          sod-instance-initializer
@@@ -730,8 -701,9 +750,11 @@@ c-fragment-tex
    c-fragment
  c-function-arguments
    c-function-type
 +c-function-keywords
 +  c-keyword-function-type
+ c-qualifier-keyword
+   (eql :atomic)
+   cl:symbol
  c-tagged-type-kind
    c-enum-type
    c-struct-type
@@@ -743,10 -715,12 +766,13 @@@ c-type-clas
  c-type-equal-p
    t t
    c-array-type c-array-type
+   c-atomic-type c-atomic-type
    c-class-type c-class-type
    c-function-type c-function-type
 +  c-keyword-function-type c-keyword-function-type
    c-pointer-type c-pointer-type
+   c-storage-specifiers-type c-type
+   c-type c-storage-specifiers-type
    qualifiable-c-type qualifiable-c-type
    simple-c-type simple-c-type
    tagged-c-type tagged-c-type
@@@ -754,10 -728,14 +780,14 @@@ c-type-nam
    simple-c-type
  c-type-qualifiers
    qualifiable-c-type
+ c-type-specifiers
+   c-storage-specifiers-type
  c-type-subtype
    c-array-type
+   c-atomic-type
    c-function-type
    c-pointer-type
+   c-storage-specifiers-type
  c-type-tag
    tagged-c-type
  chain-offset-chain-head
@@@ -847,7 -825,6 +877,7 @@@ compute-chain
  compute-cpl
    sod-class
  compute-effective-method-body
 +  basic-effective-method t t
    simple-effective-method t t
  compute-effective-methods
    sod-class
@@@ -862,8 -839,7 +892,7 @@@ compute-islot
    sod-class sod-class
  compute-method-entry-functions
    basic-effective-method
 -  simple-effective-method
 +  effective-method
-   simple-effective-method
  compute-sod-effective-method
    sod-message sod-class
  compute-vtable
@@@ -895,11 -871,6 +924,11 @@@ effective-method-clas
    effective-method
  effective-method-function-name
    effective-method
 +effective-method-keywords
 +  effective-method
 +effective-method-live-p
 +  sod::lifecycle-effective-method
 +  simple-effective-method
  effective-method-message
    effective-method
  effective-slot-class
@@@ -921,6 -892,12 +950,12 @@@ ensure-sequencer-ite
    sequencer t
  ensure-var
    sod::basic-codegen t t
+ expand-c-storage-specifier
+   cl:list
+   cl:symbol
+ expand-c-storage-specifier-form
+   (eql alignas) t
+   (eql sod-parser:lisp) t
  expand-c-type-form
    (eql cl:*) t
    (eql cl:array) t
    (eql cl:function) t
    (eql cl:nil) t
    (eql cl:union) t
+   (eql atomic) t
    (eql bool) t
    (eql double) t
    (eql double-complex) t
    (eql short) t
    (eql signed-char) t
    (eql size-t) t
+   (eql specs) t
    (eql struct) t
    (eql unsigned) t
    (eql unsigned-char) t
@@@ -1026,7 -1005,6 +1063,7 @@@ sod-parser:file-locatio
    c-fragment
    property
    sod-class
 +  sod-initarg
    sod-initializer
    sod-message
    sod-method
@@@ -1035,8 -1013,6 +1072,8 @@@ finalize-modul
    module
  finalize-sod-class
    sod-class
 +find-slot-initargs
 +  sod-class sod-slot
  find-slot-initializer
    sod-class sod-slot
  format-temporary-name
@@@ -1101,19 -1077,12 +1138,19 @@@ ilayout-clas
  ilayout-ichains
    ilayout
  inst-alt
 +  cond-inst
    if-inst
  inst-args
 +  banner-inst
    call-inst
 +inst-banner
 +  function-inst
 +inst-banner-args
 +  function-inst
  inst-body
    block-inst
    do-while-inst
 +  for-inst
    function-inst
    while-inst
  inst-chain-head
  inst-class
    convert-to-ilayout-inst
  inst-cond
 +  cond-inst
    do-while-inst
 +  for-inst
    if-inst
    while-inst
  inst-conseq
 +  cond-inst
    if-inst
 +inst-control
 +  banner-inst
  inst-decls
    block-inst
  inst-expr
  inst-func
    call-inst
  inst-init
 +  for-inst
    var-inst
  inst-metric
    cl:list
    cl:null
    t
 +  banner-inst
    block-inst
    break-inst
    call-inst
 +  cond-inst
    continue-inst
    convert-to-ilayout-inst
    do-while-inst
    expr-inst
 +  for-inst
    function-inst
    if-inst
    return-inst
    set-inst
 +  sod::suppliedp-struct-inst
    update-inst
    var-inst
    while-inst
@@@ -1174,11 -1133,8 +1211,11 @@@ inst-o
  inst-type
    function-inst
    var-inst
 +inst-update
 +  for-inst
  inst-var
    set-inst
 +  sod::suppliedp-struct-inst
    update-inst
  invoke-sequencer-items
    sequencer
@@@ -1202,16 -1158,12 +1239,16 @@@ make-method-entrie
    basic-effective-method sod-class sod-class
  sod-parser:make-scanner-stream
    sod-token-scanner
 +make-sod-class-initfrag
 +  sod-class t t
  make-sod-class-initializer
 -  sod-class t t t t t
 +  sod-class t t t t
 +make-sod-class-tearfrag
 +  sod-class t t
  make-sod-initializer-using-slot
 -  sod-class sod-slot t t t t t
 +  sod-class sod-slot t t t t
  make-sod-instance-initializer
 -  sod-class t t t t t
 +  sod-class t t t t
  make-sod-message
    sod-class t t t
  make-sod-method
@@@ -1220,12 -1172,6 +1257,12 @@@ make-sod-method-using-messag
    sod-message sod-class t t t t
  make-sod-slot
    sod-class t t t
 +make-sod-slot-initarg
 +  sod-class t t t t
 +make-sod-slot-initarg-using-slot
 +  sod-class t sod-slot t
 +make-sod-user-initarg
 +  sod-class t t t
  method-entry-chain-head
    method-entry
  method-entry-chain-tail
@@@ -1238,9 -1184,6 +1275,9 @@@ method-entry-function-typ
    method-entry
  method-entry-slot-name
    method-entry
 +method-keyword-argument-lists
 +  effective-method t
 +  sod::initialization-effective-method t
  module-dependencies
    module
  (setf module-dependencies)
@@@ -1258,26 -1201,35 +1295,37 @@@ module-nam
    module
  module-pset
    module
+ pprint-c-storage-specifier
+   cl:symbol t
+   alignas-storage-specifier t
  pprint-c-type
    t t t
    c-array-type t t
+   c-atomic-type t t
    c-function-type t t
 +  c-keyword-function-type t t
    c-pointer-type t t
+   c-storage-specifiers-type t t
    simple-c-type t t
    tagged-c-type t t
  primary-method-class
    simple-message
    standard-message
+ print-c-storage-specifier
+   t cl:symbol
+   t t
+   t alignas-storage-specifier
  print-c-type
    t c-array-type
+   t c-atomic-type
    t c-class-type
    t c-function-type
    t c-pointer-type
+   t c-storage-specifiers-type
    t simple-c-type
    t tagged-c-type
  cl:print-object
 +  banner-inst t
    base-offset t
    block-inst t
    break-inst t
    call-inst t
    chain-offset t
    class-pointer t
 +  cond-inst t
    continue-inst t
    convert-to-ilayout-inst t
    do-while-inst t
    effective-method t
    effective-slot t
    expr-inst t
 +  for-inst t
    function-inst t
    ichain t
    if-inst t
@@@ -1333,8 -1283,6 +1381,8 @@@ cl:shared-initializ
    aggregating-message t
    basic-direct-method t
    c-function-type t
 +  c-keyword-function-type t
 +  effective-method t
    method-codegen t
    module t
    sequencer t
    sod-token-scanner t
  simple-method-body
    aggregating-effective-method t t
 +  sod::lifecycle-effective-method t t
    standard-effective-method t t
  cl:slot-unbound
    t basic-direct-method (eql sod::function-type)
    t basic-effective-method (eql sod::basic-argument-names)
    t basic-effective-method (eql sod::functions)
    t basic-message (eql sod::argument-tail)
 -  t basic-message (eql sod::no-varargs-tail)
    t delegating-direct-method (eql sod::function-type)
    t delegating-direct-method (eql sod::next-method-type)
    t sod-class (eql sod::%ilayout)
@@@ -1376,10 -1324,6 +1424,10 @@@ sod-class-direct-superclasse
    sod-class
  sod-class-ilayout
    sod-class
 +sod-class-initfrags
 +  sod-class
 +(setf sod-class-initfrags)
 +  t sod-class
  sod-class-instance-initializers
    sod-class
  (setf sod-class-instance-initializers)
@@@ -1406,21 -1350,17 +1454,21 @@@ sod-class-slot
    t sod-class
  sod-class-state
    sod-class
 +sod-class-tearfrags
 +  sod-class
 +(setf sod-class-tearfrags)
 +  t sod-class
  sod-class-type
    sod-class
  sod-class-vtables
    sod-class
 +sod-initarg-argument
 +  sod-initarg
  sod-initializer-class
    sod-initializer
  sod-initializer-slot
    sod-initializer
 -sod-initializer-value-form
 -  sod-initializer
 -sod-initializer-value-kind
 +sod-initializer-value
    sod-initializer
  sod-message-argument-tail
    basic-message
@@@ -1430,9 -1370,7 +1478,9 @@@ sod-message-combinatio
    aggregating-message
  sod-message-effective-method-class
    aggregating-message
 +  sod::initialization-message
    standard-message
 +  sod::teardown-message
  sod-message-kernel-function
    aggregating-message
  sod-message-method-class
    sod-message sod-class t
  sod-message-name
    sod-message
 -sod-message-no-varargs-tail
 -  basic-message
  sod-message-type
    sod-message
  sod-method-body
@@@ -1576,6 -1516,7 +1624,7 @@@ parser-proto.lis
    cl:char                                       function setf c-type parser
    character-parser-context                      class
    combine-parser-failures                       function
+   commit                                        parser
    cond-parse                                    macro
    define-pluggable-parser                       macro
    defparse                                      macro
    cl:not                                        function parser
    cl:or                                         macro parser
    parse
+   parse-empty                                   function
+   parse-fail                                    function
    parser                                        macro
    parser-at-eof-p                               generic
    parser-capture-place                          generic
@@@ -1781,6 -1724,7 +1832,7 @@@ expand-parser-for
    t (eql cl:t) t
    t (eql cl:when) t
    t (eql ?) t
+   t (eql commit) t
    t (eql expr) t
    t (eql label) t
    t (eql lisp) t
@@@ -1818,7 -1762,6 +1870,7 @@@ file-locatio
    sod:c-fragment
    sod:property
    sod:sod-class
 +  sod:sod-initarg
    sod:sod-initializer
    sod:sod-message
    sod:sod-method
diff --combined doc/clang.tex
index 6f7e218d2abb09478fe0291ebf82c9b0065d4cd3,ce342653fdcc0b09fccf7975535880a3fafb6bcb..0c6c4bb6c7a9cfe8852c4555d3e4dd3915091650
@@@ -46,10 -46,10 +46,11 @@@ The class hierarchy is shown in~\xref{f
            @|c-struct-type| \\
            @|c-union-type| \\
            @|c-enum-type| \- \\
+         @|c-atomic-type| \\
          @|c-pointer-type| \- \\
        @|c-array-type| \\
 -      @|c-function-type|
 +      @|c-function-type| \\ \ind
 +        @|c-keyword-function-type| \-
    \end{tabbing}}
    \caption{Classes representing C types}
  \label{fig:codegen.c-types.classes}
@@@ -165,8 -165,16 +166,16 @@@ type specifier.  Type specifiers fit in
    type specifiers among its arguments.
  \end{describe}
  
- \begin{describe}{fun}{expand-c-type-spec @<type-spec> @> @<form>}
+ \begin{describe}{gf}{expand-c-type-spec @<type-spec> @> @<form>}
    Returns the Lisp form that @|(c-type @<type-spec>)| would expand into.
+   If @<type-spec> is a list, then \descref{expand-c-type-form}{fun} is
+   invoked.
+ \end{describe}
+ \begin{describe}{gf}{expand-c-type-form @<head> @<tail> @> @<form>}
+   Returns the Lisp form that @|(c-type (@<head> . @<tail>)| would expand
+   into.
  \end{describe}
  
  \begin{describe}{gf}
@@@ -299,6 -307,29 +308,29 @@@ argument lists for methods.  This is do
  \subsection{Type qualifiers and qualifiable types}
  \label{sec:clang.ctypes.qual}
  
+ Qualifiers -- @|const|, @|volatile|, and so on -- are represented as lists of
+ keywords attached to types.  Not all C types can carry qualifiers: notably,
+ function and array types cannot be qualified.
+ For the most part, the C qualifier keywords correspond to like-named Lisp
+ keywords, only the Lisp keyword names are in uppercase.  The correspondence
+ is shown in \xref{tab:clang.ctypes.qual}.
+ \begin{table}
+   \begin{tabular}[C]{*2{>{\codeface}l}l}                           \hlx*{hv}
+     \thd{\textbf{C name}}       & \thd{\textbf{Lisp name}}      \\ \hlx{vhv}
+     _Atomic                     & :atomic                       \\
+     const                       & :const                        \\
+     restrict                    & :restrict                     \\
+     volatile                    & :volatile                     \\ \hlx*{vh}
+   \end{tabular}
+   \caption{C and Lisp qualifier names} \label{tab:clang.ctypes.qual}
+ \end{table}
+ The default behaviour, on output, is to convert keywords to lowercase and
+ hope for the best: special cases can be dealt with by adding appropriate
+ methods to \descref{c-qualifier-keyword}{gf}.
  \begin{describe}{cls}{qualifiable-c-type (c-type) \&key :qualifiers}
    The class @|qualifiable-c-type| describes C types which can bear
    `qualifiers' (\Cplusplus\ calls them `cv-qualifiers'): @|const|,
    non-null then the final character of the returned string will be a space.
  \end{describe}
  
+ \begin{describe}{gf}{c-qualifier-keyword @<qualifier> @> @<string>}
+   Return, as a string, the C keyword corresponding to the Lisp @<qualifier>.
+   There is a standard method, which deals with many qualifiers.  Additional
+   methods exist for qualifier keywords which need special handling, such as
+   @|:atomic|; they are not listed here explicitly.
+   \begin{describe}{meth}{c-qualifier-keyword @<keyword> @> @<string>}
+     Returns the @<keyword>'s print-name, in lower case.  This is sufficient
+     for the standard qualifiers @|:const|, @|:restrict|, and @|:volatile|.
+   \end{describe}
+ \end{describe}
+ \begin{describe}{fun}{c-type-qualifier-keywords @<c-type> @> @<list>}
+   Return the @<c-type>'s qualifiers, as a list of C keyword names.
+ \end{describe}
+ \subsection{Storage specifiers} \label{sec:clang.ctypes.specs}
+ Some declaration specifiers, mostly to do with how to store the specific
+ object in question, are determinedly `top level', and, unlike qualifiers,
+ don't stay attached to the base type when acted on by declarator operators.
+ Sod calls these `storage specifiers', though no such category exists in the C
+ standard.  They have their own protocol, which is similar in many ways to
+ that of C types.
+ Every Lisp keyword is potentially a storage specifier, which simply maps to
+ its lower-case print name in C; but other storage specifiers may be more
+ complicated objects.
+ \begin{describe}{cls}
+     {c-storage-specifiers-type (c-type) \&key :subtype :specifiers}
+   A type which carries storage specifiers.  The @<subtype> is the actual
+   type, and may be any C type; the @<specifiers> are a list of
+   storage-specifier objects.
+   The type specifier @|(specs @<subtype> @<specifier>^*)| wraps the
+   @<subtype> in a @|c-storage-specifiers-type|, carrying the @<specifier>s,
+   which are a list of storage specifiers in S-expression notation.
+ \end{describe}
+ \begin{describe}{fun}{c-type-specifiers @<type> @> @<list>}
+   Returns the list of type specifiers attached to the @<type> object, which
+   must be a @|c-storage-specifiers-type|.
+ \end{describe}
+ \begin{describe}{mac}
+     {define-c-storage-specifier-syntax @<name> @<lambda-list> \\ \ind
+       @[[ @<declaration>^* @! @<doc-string> @]] \\
+       @<form>^* \-
+      \nlret @<name>}
+   Defines the symbol @<name> as a new storage-specifier operator.  When a
+   list of the form @|(@<name> @<argument>^*)| is used as a storage specifier,
+   the @<argument>s are bound to fresh variables according to the
+   @<lambda-list> (a destructuring lambda-list) and the @<form>s evaluated in
+   order in the resulting lexical environment as an implicit @<progn>.  The
+   value should be a Lisp form which will evaluate to the storage-specifier
+   object described by the arguments.
+   The @<form>s may call @|expand-c-storage-specifier| in order to recursively
+   expand storage specifiers among its arguments.
+ \end{describe}
+ \begin{describe}{gf}{expand-c-storage-specifier @<spec> @> @<form>}
+   Returns the Lisp form that @<spec> expands to within @|(c-type (specs
+   @<subtype> @<spec>))|.
+   If @<spec> is a list, then \descref{expand-c-storage-specifier-form} is
+   invoked.
+ \end{describe}
+ \begin{describe}{gf}{expand-c-storage-specifier-form @<spec> @> @<form>}
+   Returns the Lisp form that @|(@<head> . @<tail>)| expands to within
+   @|(c-type (specs @<subtype> (@<head> . @<tail>)))|.
+ \end{describe}
+ \begin{describe}{gf}{pprint-c-storage-specifier @<spec> @<stream>}
+ \end{describe}
+ \begin{describe}{gf}
+     {print-c-storage-specifier @<stream> @<spec>
+                                \&optional @<colon> @<atsign>}
+ \end{describe}
+ \begin{describe}{fun}{wrap-c-type @<func> @<base-type> @> @<c-type>}
+   Apply @<func> to the underlying C type of @<base-type> to create a new
+   `wrapped' type, and attach the storage specifiers of @<base-type> to the
+   wrapped type.
+   If @<base-type> is \emph{not} a @|c-storage-specifiers-type|, then return
+   @|(funcall @<func> @<base-type>)|.  Otherwise, return a new
+   @|c-storage-specifiers-type|, with the same specifiers, but whose subtype
+   is the result of applying @<func> to the subtype of the original
+   @<base-type>.
+ \end{describe}
+ \begin{describe}{cls}{alignas-storage-specifier () \&key :alignment}
+   The class of @|_Alignas| storage specifiers; an instance denotes the
+   specifier @|_Alignas(@<alignment>)|.  The @<alignment> parameter may be any
+   printable object, but is usually a string or C fragment.
+   The storage specifier form @|(alignas @<alignment>)| returns a storage
+   specifier @|_Alignas(@<alignment>)|, where @<alignment> is evaluated.
+ \end{describe}
  
  \subsection{Leaf types} \label{sec:clang.c-types.leaf}
  
@@@ -535,6 -673,38 +674,38 @@@ protocol
  \end{describe}
  
  
+ \subsection{Atomic types} \label{sec:clang.c-types.atomic}
+ Atomic types are compound types.  The subtype of an atomic type is simply the
+ underlying type of the object.  Note that, as far as Sod is concerned, atomic
+ types are not the same as atomic-qualified types: you must be consistent
+ about which you use.
+ \begin{describe}{cls}
+     {c-atomic-type (qualifiable-c-type) \&key :qualifiers :subtype}
+   Represents an atomic type.  An instance denotes the C type
+   @|_Atomic(@<subtype>)|.
+   The @<subtype> may be any C type.\footnote{%
+     C does not permit atomic function or array types.} %
+   Two atomic types are equal if and only if their subtypes are equal and they
+   have matching qualifiers.  It is possible, though probably not useful, to
+   have an atomic-qualified atomic type.
+   The type specifier @|(atomic @<type-spec> @<qualifier>^*)| returns a type
+   qualified atomic @<subtype>, where @<subtype> is the type specified by
+   @<type-spec> and the @<qualifier>s are qualifier keywords (which are
+   evaluated).
+ \end{describe}
+ \begin{describe}{fun}
+     {make-atomic-type @<c-type> \&optional @<qualifiers> @> @<c-atomic-type>}
+   Return an object describing the type qualified atomic @<subtype>.  If
+   @<subtype> is interned, then the returned atomic type object is interned
+   also.
+ \end{describe}
  \subsection{Pointer types} \label{sec:clang.c-types.pointer}
  
  Pointers are compound types.  The subtype of a pointer type is the type it
@@@ -624,8 -794,7 +795,8 @@@ function type is the type of the functi
    not return nil.
  \end{describe}
  
 -\begin{describe}{fun}{make-argument @<name> @<c-type> @> @<argument>}
 +\begin{describe}{fun}
 +    {make-argument @<name> @<c-type> \&optional @<default> @> @<argument>}
    Construct and a return a new @<argument> object.  The argument has type
    @<c-type>, which must be a @|c-type| object, and is named @<name>.
  
    suitable for function definitions.  If @<name> is not nil, then the
    @<name>'s print representation, with @|*print-escape*| nil, is used as the
    argument name.
 +
 +  A @<default> may be supplied.  If the argument is used in a
 +  keyword-argument list (e.g., in a \descref{c-keyword-function-type}
 +  [object]{cls}), and the @<default> value is provided and non-nil, then its
 +  (unescaped) printed representation is used to provide a default value if
 +  the keyword argument is not supplied by the caller.
  \end{describe}
  
  \begin{describe*}
      {\dhead{fun}{argument-name @<argument> @> @<name>}
 -     \dhead{fun}{argument-type @<argument> @> @<c-type>}}
 -  Accessor functions for @|argument| objects.  They return the name (for
 -  @|argument-name|) or type (for @|argument-type|) from the object, as passed
 -  to @|make-argument|.
 +     \dhead{fun}{argument-type @<argument> @> @<c-type>}
 +     \dhead{fun}{argument-default @<argument> @> @<default>}}
 +  Accessor functions for @|argument| objects.  They return the appropriate
 +  component of the object, as set by to @|make-argument|.  The @<default> is
 +  nil if no default was provided to @|make-argument|.
  \end{describe*}
  
  \begin{describe}{gf}
    \end{prog}
  \end{describe}
  
 +\begin{describe}{cls}
 +    {c-keyword-function-type (c-function-type)
 +      \&key :subtype :arguments :keywords}
 +  Represents `functions' which accept keyword arguments.  Of course, actual C
 +  functions can't accept keyword arguments directly, but this type is useful
 +  for describing messages and methods which deal with keyword arguments.
 +
 +  An instance denotes the type of C function which accepts the position
 +  argument list @<arguments>, and keyword arguments from the @<keywords>
 +  list, and returns @<subtype>.  Either or both of the @<arguments> and
 +  @<keywords> lists may be empty.  (It is important to note the distinction
 +  between a function which doesn't accept keyword arguments, and one which
 +  does but for which no keyword arguments are defined.  In particular, the
 +  latter function can be changed later to accept a keyword argument without
 +  breaking compatibility with old code.)  The @<arguments> and @<keywords>
 +  lists must \emph{not} contain @|:ellipsis| markers: a function can accept
 +  keywords, or a variable-length argument tail, but not both.
 +
 +  Keyword arguments may (but need not) have a \emph{default value} which is
 +  supplied to the function body if the keyword is omitted.
 +
 +  Keyword functions are never considered to be the same as ordinary
 +  functions.  Two keyword function types are considered to be the same if
 +  their return types are the same, and their positional argument lists consist of
 +  arguments with the same type, in the same order: the keyword arguments
 +  accepted by the functions is not significant.
 +
 +  Keyword functions are constructed using an extended version of the @|fun|
 +  specifier used for ordinary C function types.  The extended syntax is as
 +  follows.
 +  \begin{prog}
 +    (fun \=@<return-type>
 +           @{ (@<arg-name> @<arg-type>) @}^* \+ \\
 +           @{ \=:keys @{ (@<kw-name> @<kw-type> @[@<kw-default>@]) @}^*
 +                   @[. @<form>@] @! \+ \\
 +                 . @<form> @}
 +  \end{prog}
 +  where either the symbol @|:keys| appears literally in the specifier, or the
 +  @<form> evaluates to a list containing the symbol @|:keys|.  (If neither of
 +  these circumstances obtains, then the specifier constructs an ordinary
 +  function type.)
 +
 +  See the description of \descref{c-function-type}{cls} for how a trailing
 +  @<form> is handled.
 +
 +  The list of @<arg-name>s and @<arg-type>s describes the positional
 +  arguments.  The list of @<kw-name>s, @<kw-type>s and @<kw-defaults>s
 +  describes the keyword arguments.
 +\end{describe}
 +
  \begin{describe}{fun}
      {make-function-type @<subtype> @<arguments> @> @<c-function-type>}
    Construct and return a new function type, returning @<subtype> and
    accepting the @<arguments>.
 +
 +  If the @<arguments> list contains a @|:keys| marker, then a
 +  \descref{c-keyword-function-type}[object]{cls} is returned: those arguments
 +  preceding the @|:keys| marker form the positional argument list, and those
 +  following the marker form the list of keyword arguments.
 +\end{describe}
 +
 +\begin{describe}{fun}
 +    {make-keyword-function-type @<subtype> @<arguments> @<keywords>
 +      \nlret @<c-keyword-function-type>}
 +  Construct and return a new keyword-function type, returning @<subtype> and
 +  accepting the @<arguments> and @<keywords>.
  \end{describe}
  
  \begin{describe}{gf}
    @|commentify-argument-names| to the argument list of the given type.
  \end{describe}
  
 +\begin{describe}{fun}{reify-variable-argument-tail @<arguments> @> @<list>}
 +  If the @<argument> list contains an @|:ellipsis| marker, then replace it
 +  with a @|va_list|.  The name for the new argument, if any, is taken from
 +  the \descref{*sod-ap*}[variable]{var}.  The new list is returned; the
 +  original list is not modified, but may share structure with the new list.
 +\end{describe}
 +
 +\begin{describe}{fun}{merge-keyword-lists @<lists> @> @<list>}
 +  Merge a number of keyword-argument lists together and return the result.
 +
 +  The @<lists> parameter is a list consisting of a number of @|(@<args>
 +  . @<origin>)| pairs: in each pair, @<args> is a list of
 +  \descref{argument}{cls} objects, and @<origin> is either nil or an object
 +  whose printed representation describes the origin of the corresponding
 +  @<args> list, suitable for inclusion in an error message.
 +
 +  The resulting list contains exactly one argument for each distinct argument
 +  name appearing in the input @<lists>; this argument will contain the
 +  default value from the earliest occurrence in the input @<lists> of an
 +  argument with that name.
 +
 +  If the same name appears multiple times with different types, an error is
 +  signalled quoting the name, conflicting types, and (if non-nil) the origins
 +  of the offending argument objects.
 +\end{describe}
 +
 +\begin{describe}{fun}
 +    {pprint-c-function-type @<return-type> @<stream>
 +                            @<print-args> @<print-kernel>}
 +  Provides the top-level structure for printing C function types.
 +
 +  Output is written to @<stream> to describe a function type returning
 +  @<return-type>, whose declarator kernel (containing the name, and any
 +  further type operands) will be printed by @<print-kernel>, and whose
 +  arguments, if any, will be printed by @<print-args>.
 +
 +  The @<print-kernel> function is a standard kernel-printing function
 +  following the \descref{pprint-c-type}[protocol]{gf}.
 +
 +  The @<print-args> function is given a single argument, which is the
 +  @<stream> to print on.  It should not print the surrounding parentheses.
 +
 +  The output written to @<stream> looks approximately like
 +  \begin{prog}
 +    @<return-type> @<kernel>(@<args>)
 +  \end{prog}
 +\end{describe}
 +
 +\begin{describe}{fun}{pprint-argument-list @<args> @<stream> @> @<flag>}
 +  Print an argument list to @<stream>.
 +
 +  The @<args> is a list of \descref{argument}[objects]{cls}, optionally
 +  containing an @|:ellipsis| marker.  The function returns true if any
 +  arguments were actually printed.
 +\end{describe}
 +
  
  \subsection{Parsing C types} \label{sec:clang.c-types.parsing}
  
@@@ -1029,10 -1073,6 +1200,10 @@@ Temporary names are represented by obje
        @<form>^*}
  \end{describe}
  
 +\begin{describe}{fun}
 +    {format-banner-comment @<stream> @<control> \&rest @<args>}
 +\end{describe}
 +
  \begin{table}
    \begin{tabular}[C]{ll>{\codeface}l}                              \hlx*{hv}
      \thd{Class name} &
      @|set|      & @<var> @<expr>           & @<var> = @<expr>;  \\ \hlx{v}
      @|update|   & @<var> @<op> @<expr>     & @<var> @<op>= @<expr>;
                                                                  \\ \hlx{v}
 +    @|cond|     & @<cond> @<conseq> @<alt> & @<cond> ? @<conseq> : @<alt>
 +                                                                \\ \hlx{v}
      @|return|   & @<expr>                  & return @[@<expr>@];
                                                                  \\ \hlx{v}
      @|break|    & ---                      & break;             \\ \hlx{v}
      @|call|     & @<func> @|\&rest| @<args>
                                             & @<func>(@<arg>_1,
                                                       $\ldots$,
 -                                                     @<arg>_n)  \\ \hlx{vhv}
 +                                                     @<arg>_n)  \\ \hlx{v}
 +    @|banner|   & @<control> @|\&rest| @<args>
 +                                           & /* @<banner> */    \\ \hlx{vhv}
      @|block|    & @<decls> @<body>         & \{ @[@<decls>@] @<body> \}
                                                                  \\ \hlx{v}
      @|if|       & @<cond> @<conseq> @|\&optional| @<alt>
                                             & if (@<cond>) @<conseq>
                                               @[else @<alt>@]    \\ \hlx{v}
 +    @|for|      & @<init> @<cond> @<update> @<body> &
 +      for (@<init>; @<cond>; @<update>) @<body>                 \\ \hlx{v}
      @|while|    & @<cond> @<body>          & while (@<cond>) @<body>
                                                                  \\ \hlx{v}
      @|do-while| & @<body> @<cond>          & do @<body> while (@<cond>);
                                                                  \\ \hlx{v}
 -    @|function| & @<name> @<type> @<body>  &
 -      \vtop{\hbox{\strut @<type>_0 @<name>(@<type>_1 @<arg>_1, $\ldots$,
 +    @|function| &
 +      \vtop{\hbox{\strut @<name> @<type> @<body>}
 +            \hbox{\strut \quad @|\&optional @<banner>|}
 +            \hbox{\strut \quad @|\&rest| @<banner-args>}} &
 +      \vtop{\hbox{\strut @[/* @<banner> */@]}
 +            \hbox{\strut @<type>_0 @<name>(@<type>_1 @<arg>_1, $\ldots$,
                                             @<type>_n @<arg>_n @[, \dots@])}
              \hbox{\strut \quad @<body>}}                        \\ \hlx*{vh}
    \end{tabular}
  \begin{describe}{gf}{emit-decls @<codegen> @<decls>}
  \end{describe}
  
 +\begin{describe}{fun}{emit-banner @<codegen> @<control> \&rest @<args>}
 +\end{describe}
 +
  \begin{describe}{gf}{codegen-push @<codegen>}
  \end{describe}
  
diff --combined doc/structures.tex
index 42739efea5e63d254473afddbdda26b99fa1c80f,b63a3362aa28e42a7567ba4cbde9a384e141bd16..3826bdc8572e37923d3e6d25935379cd8d9f7135
@@@ -32,8 -32,8 +32,8 @@@ works very differently from the standar
  
  The concrete types described in \xref{sec:structures.common} and
  \ref{sec:structures.root} are declared by the header file @|<sod/sod.h>|.
 -The definitions described in sections \ref{sec:structures.layout} are defined
 -in the header file generated by the containing module.
 +The definitions described in \xref{sec:structures.layout} are defined in the
 +header file generated by the containing module.
  
  %%%--------------------------------------------------------------------------
  \section{Common instance structure} \label{sec:structures.common}
@@@ -92,7 -92,7 +92,7 @@@ recommended
  
  \begin{figure}[tbp]
    \begin{tabular}{p{10pt}p{10pt}}
 -    \begin{prog}
 +    \begin{nprog}
        struct SodObject__ilayout \{ \\ \ind
          union \{ \\ \ind
            struct SodObject__ichain_obj \{ \\ \ind
            \} obj; \- \\
          \} obj; \- \\
        \};
 -    \end{prog}
 +    \end{nprog}
      &
 -    \begin{prog}
 +    \begin{nprog}
        struct SodObject__vt_obj \{ \\ \ind
          const SodClass *_class; \\
 -        size_t _base; \- \\
 +        size_t _base; \\
 +        struct SodObject__vtmsgs_obj \{ \\ \ind
 +          void (*init)(SodObject *me, ...); \\
 +          void (*init__v)(SodObject *me, va_list); \\
 +          int (*teardown)(SodObject *me); \- \\
 +        \} obj; \- \\
        \};
 -    \end{prog} \\
 +    \end{nprog} \\
    \end{tabular}
    \caption{Instance and vtable layout of @|SodObject|}
    \label{fig:structures.root.sodobject}
  
  \begin{describe}[SodObject]{cls}
      {[nick = obj, metaclass = SodClass, lisp_metaclass = sod_class] \\
 -     class SodObject \{ \}}
 +     class SodObject \{ \\ \ind
 +       void init(?);
 +     \}}
  
 -  The @|SodObject| class defines no slots or messages.  Because @|SodObject|
 -  has no direct superclasses, there is only one chain, and no inherited
 -  slots or messages, so the single chain contains only a vtable pointer.
 +  The @|SodObject| class defines no slots.  Because @|SodObject| has no
 +  direct superclasses, there is only one chain, and no inherited slots or
 +  messages, so the single chain contains only a vtable pointer.
  
 -  Since there are no messages, and @|SodClass| also has only one chain, the
 -  vtable contains only the standard class pointer and offset-to-base members.
 -  In a direct instance of @|SodObject| (why would you want one?)  the class
 -  pointer contains the address of @|SodObject__class| and the offset is zero.
 +  Since @|SodClass| also has only one chain, the vtable contains only the
 +  standard class pointer and offset-to-base members.  In a direct instance of
 +  @|SodObject| (why would you want one?)  the class pointer contains the
 +  address of @|SodObject__class| and the offset is zero.
  
    The instance and vtable layout of @|SodObject| is shown in
    \xref{fig:structures.root.sodobject}.
 +
 +  The following messages are defined.
 +
 +  \begin{describe}[obj.init]{msg}{void init(?);}
 +    Initialize a newly allocated instance.
 +
 +    This message uses a custom method combination which works like the
 +    standard method combination except that default behaviour specific to the
 +    receiver's direct class is invoked if no primary or around method
 +    overrides.  This default behaviour may be invoked multiple times if some
 +    method calls on its @|next_method| function more than once.
 +
 +    This default behaviour is to initialize the instance's slots using the
 +    defined slot initializers, and execute the initialization fragments.
 +    Each slot is initialized using the most specific applicable initializer,
 +    if any.  Slots without an initializer are left uninitialized.
 +
 +    Slots are initialized and initialization fragments executed together, a
 +    superclass at a time: first, the superclass's slots are initialized (if
 +    any); then the superclass's initialization fragments (if any) are
 +    executed, starting with the least specific superclass first.  Slots and
 +    initialization fragments defined by the same class are processed in the
 +    order in which they appear in the class definition.
 +
 +    There are no standard keyword arguments; methods on subclasses are free
 +    to introduce their own in the usual way.
 +
 +    It is usual to provide complex initialization behaviour as @|after|
 +    methods.  This ensures that slots have been initialized as necessary
 +    before the method executes.
 +
 +    For more details on instance construction, see
 +    \xref{sec:concepts.lifecycle.birth}.
 +  \end{describe}
 +
 +  \begin{describe}[obj.teardown]{msg}{int teardown();}
 +    Teardown an instance which is no longer required.
 +
 +    The message returns an integer flag.  A zero value means that the
 +    instance is safe to deallocate.  A nonzero value means that the instance
 +    should not be deallocated, and that it is safe for the caller to simply
 +    forget about it.  This simple protocol may be used, for example, to
 +    implement a reference-counting system.
 +
 +    This message uses a custom method combination which works like the
 +    standard method combination except that default behaviour is invoked if
 +    no primary or around method overrides.
 +
 +    This default behaviour is to execute each superclass's teardown
 +    fragments, most specific first, and then return zero to indicate that the
 +    object is ready for deallocation.  Teardown fragments defined by the same
 +    class are processed in the order in which they appear in the class
 +    definition.
 +
 +    It is usual to provide complex teardown behaviour as @|before| methods.
 +    Logic to decide whether to allow deallocation is usually implemented as
 +    @|around| methods.
 +  \end{describe}
  \end{describe}
  
  
         const char *name; \\
         const char *nick; \\
         size_t initsz; \\
+        size_t align; \\
         void *(*imprint)(void *@<p>); \\
 -       void *(*init)(void *@<p>); \\
         size_t n_supers; \\
         const SodClass *const *supers; \\
         size_t n_cpl; \\
         size_t islotsz; \- \\
       \}}
  
 -  The @|SodClass| class defines no messages, but there are a number of slots.
 -  Its only direct superclass is @|SodObject| and so (like its superclass) its
 -  vtable is trivial.
 +  The @|SodClass| class defines no additional messages , but there are a
 +  number of slots.  Its only direct superclass is @|SodObject| and so (like
 +  its superclass) its vtable is simple.
  
    The slots defined are as follows.
    \begin{description} \let\makelabel\code
  
    \item[initsz] The size in bytes required to store an instance of the class.
  
+   \item[align] A sufficient alignment for the class's instance storage.
    \item[imprint] A pointer to a function: given a pointer @<p> to at least
      @<initsz> bytes of appropriately aligned memory, `imprint' this memory it
      so that it becomes a minimally functional instance of the class: all of
      the vtable and class pointers are properly initialized, but the slots are
      left untouched.  The function returns its argument @<p>.
  
 -  \item[init] A pointer to a function: given a pointer @<p> to at least
 -    @<initsz> bytes of appropriately aligned memory, initialize an instance
 -    of the class in it: all of the vtable and class pointers are initialized,
 -    as are slots for which initializers are defined.  Other slots are left
 -    untouched.  The function returns its argument @<p>.
 -
    \item[n_supers] The number of direct superclasses.  (This is zero exactly
      in the case of @|SodObject|.)
  
@@@ -365,7 -310,7 +368,7 @@@ type @|struct $C$__ilayout|
      union $B$__ichainu_$i$ $i$; \\
      \quad$\vdots$ \- \\
    \};
 -  \\[\bigskipamount]
 +  \\+
    typedef struct $C$__ichain_$h$ $C$;
  \end{prog}
  
@@@ -470,7 -415,7 +473,7 @@@ structure
        \quad$\vdots$ \- \\
      \} $c$; \- \\
    \};
 -  \\[\bigskipamount]
 +  \\+
    extern const union $C$__vtu_$h$ $C$__vtable_$h$;
  \end{prog}
  
@@@ -584,16 -529,10 +587,16 @@@ defined a
  \begin{prog}
    @<type>_0 $m$(@<type>_1 @<arg>_1, $\ldots$, @<type>_n @<arg>_n, \dots);
  \end{prog}
 +or a standard message which takes keyword arguments, defined as
 +\begin{prog}
 +  @<type>_0 $m$(\=@<type>_1 @<arg>_1, $\ldots$, @<type>_n @<arg>_n? \+ \\
 +                  @<type>_{n+1} @<kw>_{n+1} @[= @<dflt>_{n+1}@], $\ldots$,
 +                  @<type>_m @<kw>_m @[= @<dflt>_m@]);
 +\end{prog}
  two entry points are defined: the usual `main' entry point which accepts a
  variable number of arguments, and a `valist' entry point which accepts an
  argument of type @|va_list| in place of the variable portion of the argument
 -list.
 +list or keywords.
  \begin{prog}
    @<type>_0 $m$($C$ *me, @<type>_1 @<arg>_1, $\ldots$,
                  @<type>_n @<arg>_n, \dots); \\
@@@ -614,10 -553,10 +617,10 @@@ For each message $m$ directly defined b
  which makes sending the message $m$ to an instance of (any subclass of) $C$
  somewhat less ugly.
  
 -If $m$ takes a variable number of arguments, the macro is more complicated
 -and is only available in compilers advertising C99 support, but the effect is
 -the same.  For each variable-argument message, there is also an additional
 -macro for calling the `valist' entry point.
 +If $m$ takes a variable number of arguments, or keyword arguments, the macro
 +is more complicated and is only available in compilers advertising C99
 +support, but the effect is the same.  For each variable-argument message,
 +there is also an additional macro for calling the `valist' entry point.
  \begin{prog}
    \#define $C$_$m$__v(@<me>, $\ldots$, @<sod__ap>)
      @<me>@->_vt@->$c$.$m$__v(@<me>, $\ldots$, @<sod__ap>)
diff --combined doc/syntax.tex
index 3034b1e9caf393c9bca91e80361ef28e89f63af8,b8ae7977792376c4751e0ef297901076e734b124..21f835e618ebe801a398ecd3700f42fbeff93627
@@@ -451,8 -451,19 +451,19 @@@ recognized
  \alt "bool" | "_Bool"
  \alt "imaginary" | "_Imaginary" | "complex" | "_Complex"
  \alt <qualifier>
+ \alt <storage-specifier>
+ \alt <atomic-type>
  
- <qualifier> ::= "const" | "volatile" | "restrict"
+ <qualifier> ::= <atomic> | "const" | "volatile" | "restrict"
+ <atomic-type> ::=
+   <atomic> "(" @<declaration-specifier>^+ <abstract-declarator> ")"
+ <atomic> ::= "atomic" | "_Atomic"
+ <storage-specifier> ::= <alignas> "(" <c-fragment> ")"
+ <alignas> ::= "alignas" "_Alignas"
  
  <type-name> ::= <identifier>
  \end{grammar}
@@@ -469,9 -480,11 +480,11 @@@ defined in the built-in module
  
  Declaration specifiers may appear in any order.  However, not all
  combinations are permitted.  A declaration specifier must consist of zero or
- more @<qualifier>s, and one of the following, up to reordering.
+ more @<qualifier>s, zero or more @<storage-specifier>s, and one of the
+ following, up to reordering.
  \begin{itemize}
  \item @<type-name>
+ \item @<atomic-type>
  \item @"struct" @<identifier>, @"union" @<identifier>, @"enum" @<identifier>
  \item @"void"
  \item @"_Bool", @"bool"
@@@ -493,27 -506,29 +506,30 @@@ All of these have their usual C meaning
  
  \subsubsection{Declarators}
  \begin{grammar}
 -<declarator>$[k]$ ::= @<pointer>^* <primary-declarator>$[k]$
 +<declarator>$[k, a]$ ::= @<pointer>^* <primary-declarator>$[k, a]$
  
 -<primary-declarator>$[k]$ ::= $k$
 -\alt "(" <primary-declarator>$[k]$ ")"
 -\alt <primary-declarator>$[k]$ @<declarator-suffix>
 +<primary-declarator>$[k, a]$ ::= $k$
 +\alt "(" <primary-declarator>$[k, a]$ ")"
 +\alt <primary-declarator>$[k, a]$ @<declarator-suffix>$[a]$
  
  <pointer> ::= "*" @<qualifier>^*
  
 -<declarator-suffix> ::= "[" <c-fragment> "]"
 -\alt "(" <arguments> ")"
 +<declarator-suffix>$[a]$ ::= "[" <c-fragment> "]"
 +\alt "(" $a$ ")"
  
  <argument-list> ::= $\epsilon$ | "..."
  \alt <list>$[\mbox{@<argument>}]$ @["," "..."@]
  
  <argument> ::= @<declaration-specifier>^+ <argument-declarator>
  
 -<abstract-declarator> ::= <declarator>$[\epsilon]$
++<abstract-declarator> ::= <declarator>$[\epsilon, \mbox{@<argument-list>}]$
+ <argument-declarator> ::= <declarator>$[\mbox{@<identifier> @! $\epsilon$}]$
 +<argument-declarator> ::=
 +  <declarator>$[\mbox{@<identifier> @! $\epsilon$}, \mbox{@<argument-list>}]$
  
 -<simple-declarator> ::= <declarator>$[\mbox{@<identifier>}]$
 -
 -<dotted-name> ::= <identifier> "." <identifier>
 +<simple-declarator> ::=
 +  <declarator>$[\mbox{@<identifier>}, \mbox{@<argument-list>}]$
  \end{grammar}
  
  The declarator syntax is taken from C, but with some differences.
  The remaining differences are (I hope) a matter of presentation rather than
  substance.
  
 +There is additional syntax to support messages and methods which accept
 +keyword arguments.
 +
 +\begin{grammar}
 +<keyword-argument> ::= <argument> @["=" <c-fragment>@]
 +
 +<keyword-argument-list> ::=
 +  @[<list>$[\mbox{@<argument>}]$@]
 +  "?" @[<list>$[\mbox{@<keyword-argument>}]$@]
 +
 +<method-argument-list> ::= <argument-list> @! <keyword-argument-list>
 +
 +<dotted-name> ::= <identifier> "." <identifier>
 +
 +<keyword-declarator>$[k]$ ::=
 +  <declarator>$[k, \mbox{@<method-argument-list>}]$
 +\end{grammar}
 +
  
  \subsection{Class definitions} \label{sec:syntax.module.class}
  
@@@ -585,8 -582,6 +601,8 @@@ class Sub : Super 
  
  <class-item> ::= <slot-item>
  \alt <initializer-item>
 +\alt <initarg-item>
 +\alt <fragment-item>
  \alt <message-item>
  \alt <method-item>
  \end{grammar}
@@@ -664,9 -659,9 +680,9 @@@ class Example : Super 
  \begin{grammar}
  <initializer-item> ::= @["class"@] <list>$[\mbox{@<slot-initializer>}]$ ";"
  
 -<slot-initializer> ::= <dotted-name> "=" <initializer>
 +<slot-initializer> ::= <dotted-name> @["=" <initializer>@]
  
 -<initializer> :: "{" <c-fragment> "}" | <c-fragment>
 +<initializer> :: <c-fragment>
  \end{grammar}
  
  An @<initializer-item> provides an initial value for one or more slots.  If
@@@ -677,28 -672,15 +693,28 @@@ The first component of the @<dotted-nam
  class's superclasses (including itself); the second must be the name of a
  slot defined in that superclass.
  
 -The initializer has one of two forms.
 -\begin{itemize}
 -\item A @<c-fragment> enclosed in braces denotes an aggregate initializer.
 -  This is suitable for initializing structure, union or array slots.
 -\item A @<c-fragment> \emph{not} beginning with an open brace is a `bare'
 -  initializer, and continues until the next @`,' or @`;' which is not within
 -  nested brackets.  Bare initializers are suitable for initializing scalar
 -  slots, such as pointers or integers, and strings.
 -\end{itemize}
 +An @|initarg| property may be set on an instance slot initializer (or a
 +direct slot definition).  See \xref{sec:concepts.lifecycle.birth} for the
 +details.  An initializer item must have either an @|initarg| property, or an
 +initializer expression, or both.
 +
 +Each class may define at most one initializer item with an explicit
 +initializer expression for a given slot.
 +
 +\subsubsection{Initarg items}
 +\begin{grammar}
 +<initarg-item> ::=
 +  "initarg"
 +  @<declaration-specifier>^+
 +  <list>$[\mbox{@<init-declarator>}]$ ";"
 +\end{grammar}
 +
 +\subsubsection{Fragment items}
 +\begin{grammar}
 +<fragment-item> ::= <fragment-kind> "{" <c-fragment> "}"
 +
 +<fragment-kind> ::= "init" | "teardown"
 +\end{grammar}
  
  \subsubsection{Message items}
  \begin{grammar}
diff --combined lib/sod-structs.3
index 029cabde8be5ec4b0a9e9c481cbe5816e79609ce,5624d337fe1967276408207565010a958e49b7f2..35f668f471e6024f060b57d12b324ddedfd29567
@@@ -69,11 -69,6 +69,11 @@@ struct sod_vtable 
  struct SodObject__vt_obj {
  \h'2n'const SodClass *_class;
  \h'2n'size_t _base;
 +\h'2n'struct SodObject__vtmsgs_obj {
 +\h'4n'void (*init)(SodObject *\fIme\fB, ...);
 +\h'4n'void (*init__v)(SodObject *\fIme\fB, va_list);
 +\h'4n'int (*teardown)(SodObject *\fIme\fB);
 +\h'2n'} obj;
  };
  
  struct SodObject__ilayout {
@@@ -90,11 -85,6 +90,11 @@@ extern const struct SodClass__ilayout S
  struct SodClass__vt_obj {
  \h'2n'const SodClass *_class;
  \h'2n'size_t _base;
 +\h'2n'struct SodClass__vtmsgs_obj {
 +\h'4n'void (*init)(SodClass *\fIme\fB, ...);
 +\h'4n'void (*init__v)(SodClass *\fIme\fB, va_list);
 +\h'4n'int (*teardown)(SodClass *\fIme\fB);
 +\h'2n'} obj;
  };
  
  struct SodObject__ilayout {
  \h'8n'const char *name;
  \h'8n'const char *nick;
  \h'8n'size_t initsz;
+ \h'8n'size_t align;
  \h'8n'void *(*imprint)(void *\fIp\fB);
 -\h'8n'void *(*init)(void *\fIp\fB);
  \h'8n'size_t n_supers;
  \h'8n'const SodClass *const *supers;
  \h'8n'size_t n_cpl;
@@@ -221,7 -213,7 +222,7 @@@ and not really to be recommended
  .SS The SodObject class
  The
  .B SodObject
 -class defines no slots or messages.
 +class defines no slots.
  Because
  .B SodObject
  has no direct superclasses,
@@@ -229,7 -221,8 +230,7 @@@ there is only one chain
  and no inherited slots or messages,
  so the single chain contains only a vtable pointer.
  .PP
 -Since there are no messages,
 -and
 +Since
  .B SodClass
  also has only one chain,
  the vtable contains only the standard class pointer and offset-to-base
@@@ -240,79 -233,6 +241,79 @@@ In an actual instance o
  the class pointer contains the address of
  .B SodObject__class
  and the offset is zero.
 +.PP
 +The
 +.B init
 +message is used to initialize a newly allocated instance.
 +.PP
 +This message uses a custom method combination
 +which works like the standard method combination
 +except that default behaviour
 +specific to the receiver's direct class
 +is invoked if no primary or around method overrides.
 +This default behaviour may be invoked multiple times
 +if some method calls on its
 +.B next_method
 +function more than once.
 +.PP
 +This default behaviour is to initialize the instance's slots
 +using the defined slot initializers,
 +and execute the initialization fragments.
 +Each slot is initialized
 +using the most specific applicable initializer,
 +if any.
 +Slots without an initializer
 +are left uninitialized.
 +.PP
 +Slots are initialized and initialization fragments executed together,
 +a superclass at a time:
 +first, the superclass's slots are initialized (if any);
 +then the superclass's initialization fragments (if any) are executed,
 +starting with the least specific superclass first.
 +Slots and initialization fragments defined by the same class
 +are processed in the order in which they appear in the class definition.
 +.PP
 +There are no standard keyword arguments;
 +methods on subclasses are free to
 +introduce their own in the usual way.
 +.PP
 +It is usual to provide complex initialization behaviour as
 +.B after
 +methods.
 +This ensures that slots have been initialized as necessary
 +before the method executes.
 +.PP
 +The
 +.B teardown
 +message is used to tear down an instance which is no longer required.
 +.PP
 +The message returns an integer flag.
 +A zero value means that the instance is safe to deallocate.
 +A nonzero value means that the instance should not be deallocated,
 +and that it is safe for the caller to simply forget about it.
 +This simple protocol may be used, for example,
 +to implement a reference-counting system.
 +.PP
 +This message uses a custom method combination
 +which works like the standard method combination
 +except that default behaviour is invoked if
 +no primary or around method overrides.
 +This default behaviour is to execute
 +each superclass's teardown fragments,
 +most specific first,
 +and then return zero to indicate
 +that the object is ready for deallocation.
 +Teardown fragments defined by the same class
 +are processed in the order in which they appear
 +in the class definition.
 +.PP
 +It is usual to provide complex teardown behaviour as
 +.B before
 +methods.
 +Logic to decide whether to allow deallocation
 +is usually implemented as
 +.B around
 +methods.
  .
  .SS The SodClass class
  The
@@@ -334,6 -254,9 +335,9 @@@ A pointer to the class's nickname
  .B size_t initsz;
  The size in bytes required to store an instance of the class.
  .TP
+ .B size_t align;
+ A sufficient alignment for the class's instance storage.
+ .TP
  .BI "void *(*imprint)(void *" p );
  A pointer to a function:
  given a pointer
@@@ -348,6 -271,20 +352,6 @@@ but the slots are left untouched
  The function returns its argument
  .IR p .
  .TP
 -.BI "void *(*init)(void *" p );
 -A pointer to a function:
 -given a pointer
 -.I p
 -to at least
 -.I initsz
 -bytes of appropriately aligned memory,
 -initialize an instance of the class in it:
 -all of the vtable and class pointers are initialized,
 -as are slots for which initializers are defined.
 -Other slots are left untouched.
 -The function returns its argument
 -.IR p .
 -.TP
  .B size_t n_supers;
  The number of direct superclasses.
  (This is zero exactly in the case of
@@@ -960,27 -897,6 +964,27 @@@ defined a
  .IB an , 
  .B ...);
  .PP
 +or a standard message which takes keyword arguments,
 +defined as
 +.IP
 +.I tr
 +.IB m ( \c
 +.I t1
 +.IB a1 ,
 +.RB ... ,
 +.I tn
 +.IB an ?\&
 +.IR tn +1
 +.IR kn +1
 +.RB [ =
 +.IR dn +1] \c
 +.B ,
 +.I tm
 +.I km
 +.RB [ =
 +.IR dm ] \c
 +);
 +.PP
  two entry points are defined:
  the usual `main' entry point
  which accepts a variable number of
@@@ -988,8 -904,7 +992,8 @@@ arguments
  and a `valist' entry point
  which accepts an argument of type
  .B va_list
 -in place of the variable portion of the argument list.
 +in place of the variable portion of the argument list
 +or keywords.
  .IP
  .I tr
  .BI (* m )( \c
@@@ -1039,7 -954,6 +1043,7 @@@ somewhat less ugly
  If
  .I m
  takes a variable number of arguments,
 +or keyword arguments,
  the macro is more complicated
  and is only available in compilers advertising C99 support,
  but the effect is the same.
diff --combined lib/sod.h
index 894581034835f267486aa2c085a203db6a749f3c,943ed4b2434f43e449a5d3e1708c14195e4540bb..45886261949e4f9eb1984fa1c97b57832d1e5895
+++ b/lib/sod.h
  
  /*----- Preliminary utilities ---------------------------------------------*/
  
+ /* Various hacks for checking compiler versions. */
+ #define SOD__GCC_P(maj, min)                                          \
+       (__GNUC__ > (maj) || (__GNUC__ == (maj) && __GNUC_MINOR__ >= (min)))
+ #ifdef __GNUC__
+ #  define SOD__EXTENSION __extension__
+ #else
+ #  define SOD__EXTENSION
+ #endif
  /* --- @SOD__HAVE_VARARGS_MACROS@ --- *
   *
   * Use:               Defined if the compiler supports C99-style variadic macros.
@@@ -49,7 -59,7 +59,7 @@@
  
  #  define SOD__HAVE_VARARGS_MACROS
  
- #elif __GNUC__ >= 3
+ #elif SOD__GCC_P(3, 0)
     /* We're using GCC, which is trying to deny it but we don't believe it.
      * Unfortunately there's a fly in the ointment: if `-pedantic' -- or,
      * worse, `-pedantic-errors' -- is set, then GCC will warn about these
  /* We're going to want to make use of this ourselves. */
  SOD__VARARGS_MACROS_PREAMBLE
  
+ /* --- @SOD__ALIGNOF@ --- *
+  *
+  * Arguments: @type@ = a C type name, consisting of declaration specifiers
+  *                    and `*[QUALIFIERS]' declarator operators
+  *
+  * Returns:   A sufficient alignment for objects of the given @type@, as a
+  *            @size_t@.
+  */
+ #if __STDC_VERSION__ >= 201112
+ #  define SOD__ALIGNOF(type) _Alignof(type)
+ #elif SOD__GCC_P(4, 7)
+ #  define SOD__ALIGNOF(type) __extension__ _Alignof(type)
+ #elif defined(__GNUC__)
+ #  define SOD__ALIGNOF(type) __alignof__(type)
+ #else
+ #  define SOD__ALIGNOF(type)                                          \
+ offsetof(struct { char sod__x; type sod__y; }, sod__y)
+ #endif
 +/* --- @SOD__IGNORE@ --- *
 + *
 + * Arguments: @var@ = some variable name
 + *
 + * Use:               Suppress any warning that @var@ isn't used.
 + */
 +
 +#define SOD__IGNORE(var) ((void)(var))
 +
  /* --- @SOD__CAR@ --- *
   *
   * Arguments: @...@ = a nonempty list of arguments
  #include <stdarg.h>
  #include <stddef.h>
  
 +#include "keyword.h"
  #include "sod-base.h"
  
  /*----- Data structures ---------------------------------------------------*/
@@@ -236,42 -256,18 +266,42 @@@ struct sod_chain 
  
  #define SOD_CONVERT(cls, obj) ((cls *)sod_convert(cls##__class, (obj)))
  
 +/* --- @SOD_INIT@ --- *
 + *
 + * Arguments: @cls@ = a class type name
 + *            @p@ = pointer to storage to initialize
 + *            @keys@ = a @KWARGS(...)@ keyword argument sequence
 + *
 + * Use:               Initializes raw storage as an instance of @cls@.
 + */
 +
 +#define SOD_INIT(cls, p, keys) ((cls *)sod_init(cls##__class, (p), keys))
 +
 +/* --- @SOD_MAKE@ --- *
 + *
 + * Arguments: @cls@ = a class type name
 + *            @keys@ = a @KWARGS(...)@ keyword argument sequence
 + *
 + * Use:               Allocates (using @malloc@) eand initializes storage to be an
 + *            instance of @cls@.  Returns a null pointer if allocation
 + *            fails.  Use @sod_destroy@ to release the instance.
 + */
 +
 +#define SOD_MAKE(cls, keys) ((cls *)sod_make(cls##__class, keys))
 +
  /* --- @SOD_DECL@ --- *
   *
 - * Arguments: @cls_@ = a class type name
 - *            @var_@ = a variable name
 + * Arguments: @cls@ = a class type name
 + *            @var@ = a variable name
 + *            @keys@ = a @KWARGS(...)@ keyword argument sequence
   *
 - * Use:               Declare @var_@ as a pointer to an initialized instance of
 - *            @cls_@ with automatic lifetime.
 + * Use:               Declare @var@ as a pointer to an initialized instance of
 + *            @cls@ with automatic lifetime.
   */
  
 -#define SOD_DECL(cls_, var_)                                          \
 -  struct cls_##__ilayout var_##__layout;                              \
 -  cls_ *var_ = cls_##__class->cls.init(&var_##__layout)
 +#define SOD_DECL(cls, var, keys)                                      \
 +  struct cls##__ilayout var##__layout;                                        \
 +  cls *var = (cls *)sod_init(cls##__class, &var##__layout, keys)
  
  /*----- Functions provided ------------------------------------------------*/
  
@@@ -312,95 -308,6 +342,95 @@@ extern int sod_subclassp(const SodClas
  
  extern void *sod_convert(const SodClass */*cls*/, const void */*obj*/);
  
 +/* --- @sod_init@, @sod_initv@ --- *
 + *
 + * Arguments: @const SodClass *cls@ = class object for new instance
 + *            @void *p@ = pointer to storage for new instance
 + *            @va_list ap, ...@ = initialization keyword arguments
 + *
 + * Returns:   Pointer to the initialized instance.
 + *
 + * Use:               Initializes an instance in pre-allocated storage, and returns
 + *            a pointer to it.
 + *
 + *            This function will imprint the storage, and then send an
 + *            `initialize' message to the fresh instance containing the
 + *            provided keyword arguments.
 + *
 + *            It's usually convenient to use the macro @SOD_INIT@ rather
 + *            than calling @sod_init@ directly.
 + */
 +
 +extern KWCALL void *sod_init(const SodClass */*cls*/, void */*p*/, ...);
 +extern void *sod_initv(const SodClass */*cls*/, void */*p*/, va_list /*ap*/);
 +
 +/* --- @sod_make@, @sod_makev@ --- *
 + *
 + * Arguments: @const SodClass *cls@ = class object for new instance
 + *            @va_list ap, ...@ = initialization keyword arguments
 + *
 + * Returns:   Pointer to the newly-allocated initialized instance, or null.
 + *
 + * Use:               Allocates storage for a new instance, initializes it, and
 + *            returns a pointer to it.  If allocation fails, a null pointer
 + *            is returned instead.
 + *
 + *            This function will allocate the storage using @malloc@, and
 + *            then initialize it as for @sod_init@.
 + *
 + *            It's usually convenient to use the macro @SOD_MAKE@ rather
 + *            than calling @sod_make@ directly.
 + *
 + *            (This function is not available in freestanding environments
 + *            lacking @malloc@ and @free@.)
 + */
 +
 +extern KWCALL void *sod_make(const SodClass */*cls*/, ...);
 +extern void *sod_makev(const SodClass */*cls*/, va_list /*ap*/);
 +
 +/* --- @sod_teardown@ --- *
 + *
 + * Arguments: @void *p@ = pointer to an instance to be torn down
 + *
 + * Returns:   Zero if the object is torn down; nonzero if it refused for
 + *            some reason.
 + *
 + * Use:               Invokes the instance's `teardown' method to release any held
 + *            resources.
 + *
 + *            If this function returns nonzero, then the object is still
 + *            active, and may still hold important resources.  This is not
 + *            intended to be a failure condition: failures in teardown are
 + *            usually unrecoverable (or very hard to recover from) and
 + *            should probably cause the program to abort.  A refusal, on
 + *            the other hand, means that the object is still live and
 + *            shouldn't be deallocated, but that this is a normal situation
 + *            and the caller shouldn't worry about it.
 + */
 +
 +extern int sod_teardown(void */*p*/);
 +
 +/* --- @sod_destroy@ --- *
 + *
 + * Arguments: @void *p@ = pointer to an instance to be torn down, or null
 + *
 + * Returns:   Zero if the object was freed; nonzero if it refused for some
 + *            reason.
 + *
 + * Use:               Invokes the instance's `teardown' method to release any held
 + *            resources, and then calls @free@ to release the instance's
 + *            storage.  See @sod_teardown@ for details, especially
 + *            regarding the return value's meaning.
 + *
 + *            If @p@ is null, then this function does nothing except
 + *            returns zero.
 + *
 + *            (This function is not available in freestanding environments
 + *            lacking @malloc@ and @free@.)
 + */
 +
 +extern int sod_destroy(void */*p*/);
 +
  /*----- That's all, folks -------------------------------------------------*/
  
  #ifdef __cplusplus
diff --combined src/builtin.lisp
index 563766c5e8aed53eb979a05ff06b9ed180a30a1a,fbef93bd21cf5e20c5af878d5fdbaeac17d014cf..0787b8dec7ccee1ef79a47a86899f4172e1bcfbb
  (define-class-slot "initsz" (class) size-t
    (format nil "sizeof(struct ~A)" (ilayout-struct-tag class)))
  
+ (define-class-slot "align" (class) size-t
+   (format nil "SOD__ALIGNOF(struct ~A)" (ilayout-struct-tag class)))
  (define-class-slot "imprint" (class stream)
      (* (fun (* void) ("/*p*/" (* void))))
    (format nil "~A__imprint" class)
    (let ((ilayout (sod-class-ilayout class)))
      (format stream "~&~:
 -/* Imprint raw memory with instance structure. */
 -static void *~A__imprint(void *p)
 +/* Imprint raw memory with class `~A' instance structure. */
 +static void *~:*~A__imprint(void *p)
  {
    struct ~A *sod__obj = p;
  
                              (sod-class-nickname tail))))
                    (ilayout-ichains ilayout)))))
  
 -(define-class-slot "init" (class stream)
 -    (* (fun (* void) ("/*p*/" (* void))))
 -  (format nil "~A__init" class)
 -
 -  ;; FIXME this needs a metaobject protocol
 -  (let ((ilayout (sod-class-ilayout class))
 -      (used nil))
 -    (format stream "~&~:
 -/* Provide initial values for an instance's slots. */
 -static void *~A__init(void *p)~%{~%" class)
 -    (dolist (ichain (ilayout-ichains ilayout))
 -      (let ((ich (format nil "sod__obj->~A.~A"
 -                       (sod-class-nickname (ichain-head ichain))
 -                       (sod-class-nickname (ichain-tail ichain)))))
 -      (dolist (item (ichain-body ichain))
 -        (etypecase item
 -          (vtable-pointer
 -           nil)
 -          (islots
 -           (let ((isl (format nil "~A.~A"
 -                              ich
 -                              (sod-class-nickname (islots-class item)))))
 -             (dolist (slot (islots-slots item))
 -               (let ((dslot (effective-slot-direct-slot slot))
 -                     (init (effective-slot-initializer slot)))
 -                 (when init
 -                   (unless used
 -                     (format stream
 -                             "  struct ~A *sod__obj = ~A__imprint(p);~2%"
 -                             (ilayout-struct-tag class) class)
 -                     (setf used t))
 -                   (format stream "  {~%    ")
 -                   (pprint-c-type (sod-slot-type dslot) stream
 -                                  *sod-tmp-val*)
 -                   (format stream " =")
 -                   (ecase (sod-initializer-value-kind init)
 -                     (:simple (write (sod-initializer-value-form init)
 -                                     :stream stream
 -                                     :pretty nil :escape nil)
 -                              (format stream ";~%"))
 -                     (:compound (format stream " {")
 -                                (write (sod-initializer-value-form init)
 -                                       :stream stream
 -                                       :pretty nil :escape nil)
 -                                (format stream "    };~%")))
 -                   (format stream "    ~A.~A = ~A;~%  }~%"
 -                           isl (sod-slot-name dslot)
 -                           *sod-tmp-val*))))))))))
 -    (unless used
 -      (format stream "  ~A__imprint(p);~%" class))
 -    (format stream "~&~:
 -  return (p);
 -}~2%")))
 -
  ;;;--------------------------------------------------------------------------
  ;;; Superclass structure.
  
@@@ -203,296 -260,6 +206,296 @@@ static const SodClass *const ~A__cpl[] 
              (islots-struct-tag class))
        "0"))
  
 +;;;--------------------------------------------------------------------------
 +;;; Built-in methods.
 +
 +;; Common protocol.
 +
 +(defclass lifecycle-message (standard-message)
 +  ())
 +
 +(defclass lifecycle-effective-method (standard-effective-method)
 +  ())
 +
 +(defmethod effective-method-live-p ((method lifecycle-effective-method))
 +  t)
 +
 +(defgeneric lifecycle-method-kernel (method codegen target)
 +  (:documentation
 +   "Compute (into CODEGEN) the class-specific part of the METHOD.
 +
 +   The result, if any, needs to find its way to the TARGET, as usual."))
 +
 +(defmethod simple-method-body
 +    ((method lifecycle-effective-method) codegen target)
 +  (invoke-delegation-chain codegen target
 +                         (effective-method-basic-argument-names method)
 +                         (effective-method-primary-methods method)
 +                         (lambda (target)
 +                           (lifecycle-method-kernel method
 +                                                    codegen
 +                                                    target))))
 +
 +;; Utilities.
 +
 +(defun declare-me (codegen class)
 +  "Emit, to CODEGEN, a declaration of `me' as a pointer to CLASS.
 +
 +   The pointer refers to a part of the prevailing `sod__obj' object, which is
 +   assumed to be a pointer to an appropriate `ilayout' structure."
 +  (emit-decl codegen (make-var-inst "me" (c-type (* (class class)))
 +                                  (format nil "&sod__obj->~A.~A"
 +                                          (sod-class-nickname
 +                                           (sod-class-chain-head class))
 +                                          (sod-class-nickname class)))))
 +
 +(defun collect-initarg-keywords (class)
 +  "Return a list of keyword arguments corresponding to CLASS's initargs.
 +
 +   For each distinct name among the initargs defined on CLASS and its
 +   superclasses, return a single `argument' object containing the (agreed
 +   common) type, and the (unique, if present) default value from the most
 +   specific defining superclass.
 +
 +   The arguments are not returned in any especially meaningful order."
 +
 +  (let ((map (make-hash-table :test #'equal))
 +      (default-map (make-hash-table :test #'equal))
 +      (list nil))
 +    (dolist (super (sod-class-precedence-list class))
 +      (dolist (initarg (sod-class-initargs super))
 +      (let ((name (sod-initarg-name initarg))
 +            (default (sod-initarg-default initarg)))
 +        (unless (gethash name default-map)
 +          (when (or default (not (gethash name map)))
 +            (setf (gethash name map) (sod-initarg-argument initarg)))
 +          (when default
 +            (setf (gethash name default-map) t))))))
 +    (maphash (lambda (key value)
 +             (declare (ignore key))
 +             (push value list))
 +           map)
 +    list))
 +
 +(definst suppliedp-struct (stream) (flags var)
 +  (format stream
 +        "~@<struct { ~2I~_~{unsigned ~A : 1;~^ ~_~} ~I~_} ~A;~:>"
 +        flags var))
 +
 +;; Initialization.
 +
 +(defclass initialization-message (lifecycle-message)
 +  ())
 +
 +(defclass initialization-effective-method (lifecycle-effective-method)
 +  ())
 +
 +(defmethod sod-message-effective-method-class
 +    ((message initialization-message))
 +  'initialization-effective-method)
 +
 +(defmethod method-keyword-argument-lists
 +    ((method initialization-effective-method) direct-methods)
 +  (append (call-next-method)
 +        (delete-duplicates
 +         (mapcan (lambda (class)
 +                   (let ((initargs (sod-class-initargs class)))
 +                     (and initargs
 +                          (list (cons (mapcar #'sod-initarg-argument
 +                                              initargs)
 +                                      (format nil "initargs for ~A"
 +                                              class))))))
 +                 (sod-class-precedence-list
 +                  (effective-method-class method)))
 +         :key #'argument-name)))
 +
 +(defmethod lifecycle-method-kernel
 +    ((method initialization-effective-method) codegen target)
 +  (let* ((class (effective-method-class method))
 +       (keywords (collect-initarg-keywords class))
 +       (ilayout (sod-class-ilayout class))
 +       (obj-tag (ilayout-struct-tag class))
 +       (kw-tag (effective-method-keyword-struct-tag method))
 +       (kw-tail (and keywords
 +                     (list (make-argument
 +                            "sod__kw"
 +                            (c-type (* (struct kw-tag :const)))))))
 +       (func-type (c-type (fun void
 +                               ("sod__obj" (* (struct obj-tag)))
 +                               . kw-tail)))
 +       (func-name (format nil "~A__init" class))
 +       (done-setup-p nil))
 +
 +    ;; Start building the initialization function.
 +    (codegen-push codegen)
 +
 +    (labels ((set-from-initializer (var type init)
 +             ;; Store the value of INIT, which has the given TYPE, in VAR.
 +             ;; INIT has the syntax of an initializer: declare and
 +             ;; initialize a temporary, and then copy the result.
 +             ;; Compilers seem to optimize this properly.  Return the
 +             ;; resulting code as an instruction.
 +             (codegen-push codegen)
 +             (emit-decl codegen (make-var-inst *sod-tmp-val* type init))
 +             (deliver-expr codegen var *sod-tmp-val*)
 +             (codegen-pop-block codegen))
 +           (setup ()
 +             ;; Do any necessary one-time initialization required to set up
 +             ;; the environment for the initialization code.
 +             (unless done-setup-p
 +
 +               ;; Extract the keyword arguments into local variables.
 +               (when keywords
 +                 (emit-decl codegen
 +                            (make-suppliedp-struct-inst
 +                             (mapcar #'argument-name keywords)
 +                             "suppliedp"))
 +                 (emit-banner codegen "Collect the keyword arguments.")
 +                 (dolist (arg keywords)
 +                   (let* ((name (argument-name arg))
 +                          (type (argument-type arg))
 +                          (default (argument-default arg))
 +                          (kwvar (format nil "sod__kw->~A" name))
 +                          (kwset (make-set-inst name kwvar))
 +                          (suppliedp (format nil "suppliedp.~A" name)))
 +                     (emit-decl codegen (make-var-inst name type))
 +                     (deliver-expr codegen suppliedp
 +                                   (format nil "sod__kw->~A__suppliedp"
 +                                           name))
 +                     (emit-inst
 +                      codegen
 +                      (if default
 +                          (make-if-inst suppliedp kwset
 +                                        (set-from-initializer name
 +                                                              type
 +                                                              default))
 +                          kwset))))
 +
 +                 (deliver-call codegen :void
 +                               "SOD__IGNORE" "suppliedp")
 +                 (dolist (arg keywords)
 +                   (deliver-call codegen :void
 +                                 "SOD__IGNORE" (argument-name arg))))
 +
 +               (setf done-setup-p t))))
 +
 +      ;; Initialize the structure defined by the various superclasses, in
 +      ;; reverse precedence order.
 +      (dolist (super (reverse (sod-class-precedence-list class)))
 +      (let* ((ichain (find (sod-class-chain-head super)
 +                           (ilayout-ichains ilayout)
 +                           :key #'ichain-head))
 +             (islots (find super (ichain-body ichain)
 +                           :test (lambda (class item)
 +                                   (and (typep item 'islots)
 +                                        (eq (islots-class item) class)))))
 +             (frags (sod-class-initfrags super))
 +             (this-class-focussed-p nil)
 +             (isl (format nil "me->~A" (sod-class-nickname super))))
 +
 +        (flet ((focus-this-class ()
 +                 ;; Delayed initial preparation.  Don't bother defining the
 +                 ;; `me' pointer if there's actually nothing to do.
 +                 (setup)
 +                 (unless this-class-focussed-p
 +                   (emit-banner codegen
 +                                "Initialization for class `~A'." super)
 +                   (codegen-push codegen)
 +                   (declare-me codegen super)
 +                   (setf this-class-focussed-p t))))
 +
 +          ;; Work through each slot in turn.
 +          (dolist (slot (and islots (islots-slots islots)))
 +            (let ((dslot (effective-slot-direct-slot slot))
 +                  (init (effective-slot-initializer slot))
 +                  (initargs (effective-slot-initargs slot)))
 +              (when (or init initargs)
 +                (focus-this-class)
 +                (let* ((slot-type (sod-slot-type dslot))
 +                       (slot-default (sod-initializer-value init))
 +                       (target (format nil "~A.~A"
 +                                       isl (sod-slot-name dslot)))
 +                       (initinst (set-from-initializer target
 +                                                       slot-type
 +                                                       slot-default)))
 +
 +                  ;; If there are applicable initialization arguments,
 +                  ;; check to see whether they were supplied.
 +                  (dolist (initarg (reverse (remove-duplicates
 +                                             initargs
 +                                             :key #'sod-initarg-name
 +                                             :test #'string=)))
 +                    (let ((arg-name (sod-initarg-name initarg)))
 +                      (setf initinst (make-if-inst
 +                                      (format nil "suppliedp.~A" arg-name)
 +                                      (make-set-inst target arg-name)
 +                                      initinst))))
 +
 +                  (emit-inst codegen initinst)))))
 +
 +          ;; Emit the class's initialization fragments.
 +          (when frags
 +            (let ((used-me-p this-class-focussed-p))
 +              (focus-this-class)
 +              (unless used-me-p
 +                (deliver-call codegen :void "SOD__IGNORE" "me")))
 +            (dolist (frag frags)
 +              (codegen-push codegen)
 +              (emit-inst codegen frag)
 +              (emit-inst codegen (codegen-pop-block codegen))))
 +
 +          ;; If we opened a block to initialize this class then close it
 +          ;; again.
 +          (when this-class-focussed-p
 +            (emit-inst codegen (codegen-pop-block codegen)))))))
 +
 +    ;; Done making the initialization function.
 +    (codegen-pop-function codegen func-name func-type
 +                        "Instance initialization function ~:_~
 +                         for class `~A'."
 +                        class)
 +
 +    (apply #'deliver-call codegen :void func-name
 +         "sod__obj" (and keywords (list (keyword-struct-pointer))))))
 +
 +;; Teardown.
 +
 +(defclass teardown-message (lifecycle-message)
 +  ())
 +
 +(defclass teardown-effective-method (lifecycle-effective-method)
 +  ())
 +
 +(defmethod sod-message-effective-method-class ((message teardown-message))
 +  'teardown-effective-method)
 +
 +(defmethod lifecycle-method-kernel
 +    ((method teardown-effective-method) codegen target)
 +  (let* ((class (effective-method-class method))
 +       (obj-tag (ilayout-struct-tag class))
 +       (func-type (c-type (fun void ("sod__obj" (* (struct obj-tag))))))
 +       (func-name (format nil "~A__teardown" class)))
 +    (codegen-push codegen)
 +    (dolist (super (sod-class-precedence-list class))
 +      (let ((frags (sod-class-tearfrags super)))
 +      (when frags
 +        (emit-banner codegen "Teardown for class `~A'." super)
 +        (codegen-push codegen)
 +        (declare-me codegen super)
 +        (deliver-call codegen :void "SOD__IGNORE" "me")
 +        (dolist (frag frags)
 +          (codegen-push codegen)
 +          (emit-inst codegen frag)
 +          (emit-inst codegen (codegen-pop-block codegen)))
 +        (emit-inst codegen (codegen-pop-block codegen)))))
 +    (codegen-pop-function codegen func-name func-type
 +                        "Instance teardown function ~:_~
 +                         for class `~A'."
 +                        class)
 +    (deliver-call codegen :void
 +                (format nil "~A__teardown" class) "sod__obj")
 +    (deliver-expr codegen target 0)))
 +
  ;;;--------------------------------------------------------------------------
  ;;; Bootstrapping the class graph.
  
                                    (make-property-set :nick 'cls)))
         (classes (list sod-object sod-class)))
  
 +    ;; Attach the built-in messages.
 +    (make-sod-message sod-object "init"
 +                    (c-type (fun void :keys))
 +                    (make-property-set
 +                     :message-class 'initialization-message))
 +    (make-sod-message sod-object "teardown" (c-type (fun int))
 +                    (make-property-set :message-class 'teardown-message))
 +
      ;; Sort out the recursion.
      (setf (slot-value sod-class 'chain-link) sod-object)
      (dolist (class classes)
diff --combined src/c-types-impl.lisp
index 16351a3a4d67679bd9416fc34188fb11121db41e,97b6742286ea10527958e177a2234fa63268a910..fe75d7adce454e6fdfb26c25df7f88416007b595
                 (assert (gethash k map))))
             *c-type-intern-map*)))
  
+ (defun make-or-intern-c-type (new-type-class base-types &rest initargs)
+   "Return a possibly-new instance of NEW-TYPE-CLASS with the given INITARGS.
+    If all of the BASE-TYPES are interned, then use `intern-c-type' to
+    construct the new type; otherwise just make a new one with
+    `make-instance'.  BASE-TYPES may be a singleton type, or a sequence of
+    types."
+   (apply (if (if (typep base-types 'sequence)
+                (every (lambda (type)
+                         (gethash type *c-type-intern-map*))
+                       base-types)
+                (gethash base-types *c-type-intern-map*))
+            #'intern-c-type #'make-instance)
+        new-type-class
+        initargs))
+ ;;;--------------------------------------------------------------------------
+ ;;; Qualifiers.
+ (defmethod c-qualifier-keyword ((qualifier (eql :atomic))) "_Atomic")
  (defmethod qualify-c-type ((type qualifiable-c-type) qualifiers)
    (let ((initargs (instance-initargs type)))
      (remf initargs :qualifiers)
-     (apply (if (gethash type *c-type-intern-map*)
-              #'intern-c-type #'make-instance)
-          (class-of type)
+     (apply #'make-or-intern-c-type (class-of type) type
           :qualifiers (canonify-qualifiers
                        (append qualifiers (c-type-qualifiers type)))
           initargs)))
  
+ ;;;--------------------------------------------------------------------------
+ ;;; Storage specifiers.
+ (defmethod c-type-equal-p :around
+     ((type-a c-storage-specifiers-type) (type-b c-type))
+   "Ignore storage specifiers when comparing C types."
+   (c-type-equal-p (c-type-subtype type-a) type-b))
+ (defmethod c-type-equal-p :around
+     ((type-a c-type) (type-b c-storage-specifiers-type))
+   "Ignore storage specifiers when comparing C types."
+   (c-type-equal-p type-a (c-type-subtype type-b)))
+ (defun make-storage-specifiers-type (subtype specifiers)
+   "Construct a type based on SUBTYPE, carrying the storage SPECIFIERS."
+   (if (null specifiers) subtype
+       (make-or-intern-c-type 'c-storage-specifiers-type subtype
+                            :specifiers specifiers
+                            :subtype subtype)))
+ (defmethod pprint-c-type ((type c-storage-specifiers-type) stream kernel)
+   (dolist (spec (c-type-specifiers type))
+     (pprint-c-storage-specifier spec stream)
+     (write-char #\space stream)
+     (pprint-newline :miser stream))
+   (pprint-c-type (c-type-subtype type) stream kernel))
+ (defmethod print-c-type
+     (stream (type c-storage-specifiers-type) &optional colon atsign)
+   (declare (ignore colon atsign))
+   (format stream "~:@<SPECS ~@_~:I~/sod:print-c-type/~
+                           ~{ ~_~/sod:print-c-storage-specifier/~}~:>"
+         (c-type-subtype type) (c-type-specifiers type)))
+ (export 'specs)
+ (define-c-type-syntax specs (subtype &rest specifiers)
+   `(make-storage-specifiers-type
+     ,(expand-c-type-spec subtype)
+     (list ,@(mapcar #'expand-c-storage-specifier specifiers))))
+ ;;;--------------------------------------------------------------------------
+ ;;; Some storage specifiers.
+ (export 'alignas-storage-specifier)
+ (defclass alignas-storage-specifier ()
+   ((alignment :initarg :alignment :reader spec-alignment)))
+ (export 'alignas)
+ (define-c-storage-specifier-syntax alignas (alignment)
+   `(make-instance 'alignas-storage-specifier :alignment ,alignment))
+ (defmethod print-c-storage-specifier
+     (stream (spec alignas-storage-specifier) &optional colon atsign)
+   (declare (ignore colon atsign))
+   (format stream "~:@<~S ~_~S~:>" 'alignas (spec-alignment spec)))
+ (defmethod pprint-c-storage-specifier
+     ((spec alignas-storage-specifier) stream)
+   (format stream "_Alignas(~A)" (spec-alignment spec)))
  ;;;--------------------------------------------------------------------------
  ;;; Simple C types.
  
  
  (defmethod pprint-c-type ((type simple-c-type) stream kernel)
    (pprint-logical-block (stream nil)
-     (format stream "~{~(~A~) ~@_~}~A"
-           (c-type-qualifiers type)
+     (format stream "~{~A ~@_~}~A"
+           (c-type-qualifier-keywords type)
            (c-type-name type))
      (funcall kernel stream 0 t)))
  
  
  (defmethod pprint-c-type ((type tagged-c-type) stream kernel)
    (pprint-logical-block (stream nil)
-     (format stream "~{~(~A~) ~@_~}~(~A~) ~A"
-           (c-type-qualifiers type)
+     (format stream "~{~A ~@_~}~(~A~) ~A"
+           (c-type-qualifier-keywords type)
            (c-tagged-type-kind type)
            (c-type-tag type))
      (funcall kernel stream 0 t)))
          (c-type-tag type)
          (c-type-qualifiers type)))
  
+ ;;;--------------------------------------------------------------------------
+ ;;; Atomic types.
+ ;; Class definition.
+ (export 'c-atomic-type)
+ (defclass c-atomic-type (qualifiable-c-type)
+   ((subtype :initarg :subtype :type c-type :reader c-type-subtype))
+   (:documentation "C atomic types."))
+ ;; Constructor function.
+ (export 'make-atomic-type)
+ (defun make-atomic-type (subtype &optional qualifiers)
+   "Return a (maybe distinguished) atomic type."
+   (make-or-intern-c-type 'c-atomic-type subtype
+                        :subtype subtype
+                        :qualifiers (canonify-qualifiers qualifiers)))
+ ;; Comparison protocol.
+ (defmethod c-type-equal-p and ((type-a c-atomic-type) (type-b c-atomic-type))
+   (c-type-equal-p (c-type-subtype type-a) (c-type-subtype type-b)))
+ ;; C-syntax output protocol.
+ (defmethod pprint-c-type ((type c-atomic-type) stream kernel)
+   (pprint-logical-block (stream nil)
+     (format stream "~{~A ~@_~}" (c-type-qualifier-keywords type))
+     (write-string "_Atomic(" stream)
+     (pprint-indent :current 0 stream)
+     (pprint-c-type (c-type-subtype type) stream
+                  (lambda (stream prio spacep)
+                    (declare (ignore stream prio spacep))))
+     (write-char #\) stream)))
+ ;; S-expression notation protocol.
+ (defmethod print-c-type (stream (type c-atomic-type) &optional colon atsign)
+   (declare (ignore colon atsign))
+   (format stream "~:@<ATOMIC ~@_~/sod:print-c-type/~{ ~_~S~}~:>"
+         (c-type-subtype type)
+         (c-type-qualifiers type)))
+ (export 'atomic)
+ (define-c-type-syntax atomic (sub &rest quals)
+   "Return the type of atomic SUB."
+   `(make-atomic-type ,(expand-c-type-spec sub) (list ,@quals)))
  ;;;--------------------------------------------------------------------------
  ;;; Pointer types.
  
  (export 'make-pointer-type)
  (defun make-pointer-type (subtype &optional qualifiers)
    "Return a (maybe distinguished) pointer type."
-   (let ((canonical (canonify-qualifiers qualifiers)))
-     (funcall (if (gethash subtype *c-type-intern-map*)
-                #'intern-c-type #'make-instance)
-            'c-pointer-type
-            :subtype subtype
-            :qualifiers canonical)))
+   (make-or-intern-c-type 'c-pointer-type subtype
+                        :subtype subtype
+                        :qualifiers (canonify-qualifiers qualifiers)))
  
  ;; Comparison protocol.
  
                 (lambda (stream prio spacep)
                   (when spacep (c-type-space stream))
                   (maybe-in-parens (stream (> prio 1))
-                    (format stream "*~{~(~A~)~^ ~@_~}"
-                            (c-type-qualifiers type))
+                    (format stream "*~{~A~^ ~@_~}"
+                            (c-type-qualifier-keywords type))
                     (funcall kernel stream 1 (c-type-qualifiers type))))))
  
  ;; S-expression notation protocol.
                                         (argument-type arg-b)))))
              list-a list-b)))
  
 +(defun fix-and-check-keyword-argument-list (list)
 +  "Check the keyword argument LIST is valid; if so, fix it up and return it.
 +
 +   Check that the keyword arguments have distinct names.  Fix the list up by
 +   sorting it by keyword name."
 +
 +  (unless (every #'argumentp list)
 +    (error "(INTERNAL) not an argument value"))
 +
 +  (let ((list (sort (copy-list list) #'string< :key #'argument-name)))
 +    (do ((list (cdr list) (cdr list))
 +       (this (car list) (car list))
 +       (prev nil this))
 +      ((endp list))
 +      (when prev
 +      (let ((this-name (argument-name this))
 +            (prev-name (argument-name prev)))
 +        (when (string= this-name prev-name)
 +          (error "Duplicate keyword argument name `~A'." this-name)))))
 +    list))
 +
 +(export 'merge-keyword-lists)
 +(defun merge-keyword-lists (lists)
 +  "Return the union of keyword argument lists.
 +
 +   The LISTS parameter consists of pairs (ARGS . WHAT), where ARGS is a list
 +   of `argument' objects, and WHAT is either nil or a printable object
 +   describing the origin of the corresponding argument list suitable for
 +   quoting in an error message.
 +
 +   The resulting list contains exactly one argument for each distinct
 +   argument name appearing in the input lists; this argument will contain the
 +   default value corresponding to the name's earliest occurrence in the input
 +   LISTS.
 +
 +   If the same name appears in multiple input lists with different types, an
 +   error is signalled; this error will quote the origins of a representative
 +   conflicting pair of arguments."
 +
 +  ;; The easy way through all of this is with a hash table mapping argument
 +  ;; names to (ARGUMENT . WHAT) pairs.
 +
 +  (let ((argmap (make-hash-table :test #'equal)))
 +
 +    ;; Set up the table.  When we find a duplicate, check that the types
 +    ;; match.
 +    (dolist (item lists)
 +      (let ((args (car item))
 +          (what (cdr item)))
 +      (dolist (arg args)
 +        (let* ((name (argument-name arg))
 +               (other-item (gethash name argmap)))
 +          (if (null other-item)
 +              (setf (gethash name argmap) (cons arg what))
 +              (let* ((type (argument-type arg))
 +                     (other (car other-item))
 +                     (other-type (argument-type other))
 +                     (other-what (cdr other-item)))
 +                (unless (c-type-equal-p type other-type)
 +                  (error "Type mismatch for keyword argument `~A': ~
 +                          ~A~@[ (~A)~] doesn't match ~A~@[ (~A)~]."
 +                         name
 +                         type what
 +                         other-type other-what))))))))
 +
 +    ;; Now it's just a matter of picking the arguments out again.
 +    (let ((result nil))
 +      (maphash (lambda (name item)
 +               (declare (ignore name))
 +               (push (car item) result))
 +             argmap)
 +      (fix-and-check-keyword-argument-list result))))
 +
  ;; Class definition.
  
  (export '(c-function-type c-function-arguments))
              nil
              arguments))))
  
 +(export '(c-keyword-function-type c-function-keywords))
 +(defclass c-keyword-function-type (c-function-type)
 +  ((keywords :initarg :keywords :type list
 +           :reader c-function-keywords))
 +  (:documentation
 +   "C function types for `functions' which take keyword arguments."))
 +
 +(defmethod shared-initialize :after
 +    ((type c-keyword-function-type) slot-names &key (keywords nil keysp))
 +  (declare (ignore slot-names))
 +  (when keysp
 +    (setf (slot-value type 'keywords)
 +        (fix-and-check-keyword-argument-list keywords))))
 +
  ;; Constructor function.
  
  (export 'make-function-type)
  (defun make-function-type (subtype arguments)
 -  "Return a new function type, returning SUBTYPE and accepting ARGUMENTS."
 -  (make-instance 'c-function-type :subtype subtype
 -               :arguments arguments))
 +  "Return a new function type, returning SUBTYPE and accepting ARGUMENTS.
 +
 +   As a helper for dealing with the S-expression syntax for keyword
 +   functions, if ARGUMENTS has the form (ARGS ... :keys KEYWORDS ...)' then
 +   return a keyword function with arguments (ARGS ...) and keywords (KEYWORDS
 +   ...)."
 +  (let ((split (member :keys arguments)))
 +    (if split
 +      (make-instance 'c-keyword-function-type
 +                     :subtype subtype
 +                     :arguments (ldiff arguments split)
 +                     :keywords (cdr split))
 +      (make-instance 'c-function-type
 +                     :subtype subtype
 +                     :arguments arguments))))
 +
 +(export 'make-keyword-function-type)
 +(defun make-keyword-function-type (subtype arguments keywords)
 +  "Return a new keyword-function type, returning SUBTYPE and accepting
 +   ARGUMENTS and KEYWORDS."
 +  (make-instance 'c-keyword-function-type :subtype subtype
 +               :arguments arguments :keywords keywords))
  
  ;; Comparison protocol.
  
         (argument-lists-equal-p (c-function-arguments type-a)
                               (c-function-arguments type-b))))
  
 +(defmethod c-type-equal-p and
 +    ((type-a c-keyword-function-type) (type-b c-keyword-function-type))
 +  ;; Actually, there's nothing to check here.  I'm happy as long as both
 +  ;; functions notionally accept keyword arguments.
 +  t)
 +
  ;; C syntax output protocol.
  
 +(export 'pprint-c-function-type)
 +(defun pprint-c-function-type (return-type stream print-args print-kernel)
 +  "Common top-level printing for function types.
 +
 +   Prints RETURN-TYPE (KERNEL(ARGS)), where RETURN-TYPE is the actual return
 +   type, and ARGS and KERNEL are whatever is printed by the PRINT-ARGS and
 +   PRINT-KERNEL functions.
 +
 +   The PRINT-KERNEL function is the standard such thing for the
 +   `pprint-c-type' protocol; PRINT-ARGS accepts just an output stream."
 +  (pprint-c-type return-type stream
 +               (lambda (stream prio spacep)
 +                 (maybe-in-parens (stream (> prio 2))
 +                   (when spacep (c-type-space stream))
 +                   (funcall print-kernel stream 2 nil)
 +                   (pprint-indent :block 4 stream)
 +                   (pprint-newline :linear stream)
 +                   (pprint-logical-block
 +                       (stream nil :prefix "(" :suffix ")")
 +                     (funcall print-args stream))))))
 +
 +(export 'pprint-argument-list)
 +(defun pprint-argument-list (args stream)
 +  "Print an argument list.
 +
 +   The ARGS is a list of `argument' objects, optionally containing an
 +   `:ellipsis' marker.  The output is written to STREAM.
 +
 +   Returns non-nil if any arguments were actually printed."
 +  (let ((anyp nil))
 +    (pprint-logical-block (stream nil)
 +      (dolist (arg args)
 +      (if anyp
 +          (format stream ", ~_")
 +          (setf anyp t))
 +      (etypecase arg
 +        ((member :ellipsis)
 +         (write-string "..." stream))
 +        (argument
 +         (pprint-logical-block (stream nil)
 +           (pprint-c-type (argument-type arg) stream (argument-name arg))
 +           (let ((default (argument-default arg)))
 +             (when default
 +               (format stream " = ~2I~_~A" default))))))))
 +    anyp))
 +
  (let ((void-arglist (list (make-argument nil c-type-void))))
    (defmethod pprint-c-type ((type c-function-type) stream kernel)
 -    (pprint-c-type (c-type-subtype type) stream
 -                 (lambda (stream prio spacep)
 -                   (maybe-in-parens (stream (> prio 2))
 -                     (when spacep (c-type-space stream))
 -                     (funcall kernel stream 2 nil)
 -                     (pprint-indent :block 4 stream)
 -                     (pprint-logical-block
 -                         (stream nil :prefix "(" :suffix ")")
 -                       (let ((firstp t))
 -                         (dolist (arg (or (c-function-arguments type)
 -                                          void-arglist))
 -                           (if firstp
 -                               (setf firstp nil)
 -                               (format stream ", ~_"))
 -                           (if (eq arg :ellipsis)
 -                               (write-string "..." stream)
 -                               (pprint-c-type (argument-type arg)
 -                                              stream
 -                                              (argument-name arg)))))))))))
 +    (let ((args (or (c-function-arguments type) void-arglist)))
 +      (pprint-c-function-type (c-type-subtype type) stream
 +                            (lambda (stream)
 +                              (pprint-argument-list args stream))
 +                            kernel))))
 +
 +(defmethod pprint-c-type ((type c-keyword-function-type) stream kernel)
 +  (let ((args (c-function-arguments type))
 +      (keys (c-function-keywords type)))
 +    (pprint-c-function-type  (c-type-subtype type) stream
 +                             (lambda (stream)
 +                               (when (pprint-argument-list args stream)
 +                                 (format stream ", ~_"))
 +                               (write-char #\? stream)
 +                               (pprint-argument-list keys stream))
 +                             kernel)))
  
  ;; S-expression notation protocol.
  
      (stream (type c-function-type) &optional colon atsign)
    (declare (ignore colon atsign))
    (format stream "~:@<~
 -                FUN ~@_~:I~/sod:print-c-type/~
 -                ~{ ~_~:<~S ~@_~/sod:print-c-type/~:>~}~
 +                FUN ~@_~:I~
 +                ~/sod:print-c-type/~:[~; ~]~:*~_~
 +                ~<~@{~:<~S ~@_~/sod:print-c-type/~:>~^ ~_~}~:>~
 +                ~:[~2*~; ~_~S ~@_~<~@{~:<~S ~@_~/sod:print-c-type/~
 +                  ~@[ ~@_~S~]~:>~^ ~_~}~:>~]~
                  ~:>"
          (c-type-subtype type)
          (mapcar (lambda (arg)
                    (if (eq arg :ellipsis) arg
                        (list (argument-name arg) (argument-type arg))))
 -                (c-function-arguments type))))
 +                (c-function-arguments type))
 +        (typep type 'c-keyword-function-type)
 +        :keys
 +        (and (typep type 'c-keyword-function-type)
 +             (mapcar (lambda (arg)
 +                       (list (argument-name arg)
 +                             (argument-type arg)
 +                             (argument-default arg)))
 +                     (c-function-keywords type)))))
  
  (export '(fun function () func fn))
  (define-c-type-syntax fun (ret &rest args)
    "Return the type of functions which returns RET and has arguments ARGS.
  
 -   The ARGS are a list of arguments of the form (NAME TYPE).  The NAME can be
 -   NIL to indicate that no name was given.
 +   The ARGS are a list of arguments of the form (NAME TYPE [DEFAULT]).  The
 +   NAME can be NIL to indicate that no name was given.
  
     If an entry isn't a list, it's assumed to be the start of a Lisp
     expression to compute the tail of the list; similarly, if the list is
    `(make-function-type ,(expand-c-type-spec ret)
                       ,(do ((args args (cdr args))
                             (list nil
 -                                 (cons `(make-argument ,(caar args)
 -                                                       ,(expand-c-type-spec
 -                                                         (cadar args)))
 -                                       list)))
 -                          ((or (atom args) (atom (car args)))
 +                                 (if (keywordp (car args))
 +                                     (cons (car args) list)
 +                                     (let* ((name (caar args))
 +                                            (type (expand-c-type-spec
 +                                                   (cadar args)))
 +                                            (default (and (cddar args)
 +                                                          (caddar args)))
 +                                            (arg `(make-argument
 +                                                   ,name ,type ,default)))
 +                                       (cons arg list)))))
 +                          ((or (atom args)
 +                               (and (atom (car args))
 +                                    (not (keywordp (car args)))))
                             (cond ((and (null args) (null list)) `nil)
                                   ((null args) `(list ,@(nreverse list)))
 -                                 ((and (consp args)
 -                                       (eq (car args) :ellipsis))
 -                                  `(list ,@(nreverse list) :ellipsis))
                                   ((null list) `,args)
                                   (t `(list* ,@(nreverse list) ,args)))))))
  (c-type-alias fun function () func fn)
    (mapcar (lambda (arg)
            (if (eq arg :ellipsis) arg
                (make-argument (commentify-argument-name (argument-name arg))
 -                             (argument-type arg))))
 +                             (argument-type arg)
 +                             (argument-default arg))))
          arguments))
  
  (export 'commentify-function-type)
                      (commentify-argument-names
                       (c-function-arguments type))))
  
 +(export 'reify-variable-argument-tail)
 +(defun reify-variable-argument-tail (arguments)
 +  "Replace any `:ellipsis' item in ARGUMENTS with a `va_list' argument.
 +
 +   The argument's name is taken from the variable `*sod-ap*'."
 +  (substitute (make-argument *sod-ap* c-type-va-list) :ellipsis arguments))
 +
  ;;;----- That's all, folks --------------------------------------------------
diff --combined src/c-types-parse.lisp
index 6a622b7d71e02e8680dc6edbb3c42f336e804870,450cee69dde9150a4c62c8b739d59909c0aee3e2..6f5db4d86e4ce8a7ab214a52e19cc9ee275e42a4
    ;; accessor functions later.
    ((label :type keyword :initarg :label :reader ds-label)
     (name :type string :initarg :name :reader ds-name)
-    (kind :type (member type complexity sign size qualifier)
+    (kind :type (member type complexity sign size qualifier specs)
         :initarg :kind :reader ds-kind)
     (taggedp :type boolean :initarg :taggedp
            :initform nil :reader ds-taggedp))
    (:documentation
     "Represents the important components of a declaration specifier.
  
-     The only interesting instances of this class are in the table
-     `*declspec-map*'."))
+    The only interesting instances of this class are in the table
+    `*declspec-map*'."))
  
  (defmethod shared-initialize :after ((ds declspec) slot-names &key)
    "If no name is provided then derive one from the label.
  (defparameter *declspec-map*
    (let ((map (make-hash-table :test #'equal)))
      (dolist (item '((type :void :char :int :float :double
-                         (:bool :name "_Bool"))
-                   (complexity (:complex :name "_Complex")
-                               (:imaginary :name "_Imaginary"))
+                         (:bool :compat "_Bool"))
+                   (complexity (:complex :compat "_Complex")
+                               (:imaginary :compat "_Imaginary"))
                    ((type :taggedp t) :enum :struct :union)
                    (size :short :long (:long-long :name "long long"))
                    (sign :signed :unsigned)
-                   (qualifier :const :restrict :volatile)))
+                   (qualifier :const :restrict :volatile
+                              (:atomic :compat "_Atomic"))))
        (destructuring-bind (kind &key (taggedp nil))
          (let ((spec (car item)))
            (if (consp spec) spec (list spec)))
          (destructuring-bind (label
                               &key
                               (name (string-downcase label))
+                              compat
                               (taggedp taggedp))
              (if (consp spec) spec (list spec))
            (let ((ds (make-instance 'declspec
                                     :label label
-                                    :name name
+                                    :name (or compat name)
                                     :kind kind
                                     :taggedp taggedp)))
              (setf (gethash name map) ds
-                   (gethash label map) ds))))))
-     (dolist (label '(:complex :imaginary :bool))
-       (setf (gethash (string-downcase label) map) (gethash label map)))
+                   (gethash label map) ds)
+             (when compat
+               (setf (gethash compat map) ds)))))))
      map)
    "Maps symbolic labels and textual names to `declspec' instances.")
  
+ (defclass storespec ()
+   ((spec :initarg :spec :reader ds-spec))
+   (:documentation "Carrier for a storage specifier."))
+ (defmethod ds-label ((spec storespec)) spec)
+ (defmethod ds-kind ((spec storespec)) 'specs)
+ (defmethod ds-label ((ty c-type)) :c-type)
+ (defmethod ds-name ((ty c-type)) (princ-to-string ty))
+ (defmethod ds-kind ((ty c-type)) 'type)
  ;; A collection of declaration specifiers, and how to merge them together.
  
  (defclass declspecs ()
     (complexity :initform nil :initarg :complexity :reader ds-complexity)
     (sign :initform nil :initarg :sign :reader ds-sign)
     (size :initform nil :initarg :size :reader ds-size)
+    (specs :initform nil :initarg :specs :reader ds-specs)
     (qualifier :initform nil :initarg :qualifiers :reader ds-qualifiers))
-   (:documentation
-    "Represents a collection of declaration specifiers.
-     This is used during type parsing to represent the type under
-     construction.  Instances are immutable: we build new ones rather than
-     modifying existing ones.  This leads to a certain amount of churn, but
-     we'll just have to live with that.
+   (:documentation "Represents a collection of declaration specifiers.
  
-     (Why are instances immutable?  Because it's much easier to merge a new
-     specifier into an existing collection and then check that the resulting
-     thing is valid, rather than having to deal with all of the possible
-     special cases of what the new thing might be.  And if the merged
-     collection isn't good, I must roll back to the previous version.  So I
-     don't get to take advantage of a mutable structure.)"))
+    This is used during type parsing to represent the type under construction.
+    Instances are immutable: we build new ones rather than modifying existing
+    ones.  This leads to a certain amount of churn, but we'll just have to
+    live with that.
  
- (defmethod ds-label ((ty c-type)) :c-type)
- (defmethod ds-name ((ty c-type)) (princ-to-string ty))
- (defmethod ds-kind ((ty c-type)) 'type)
+    (Why are instances immutable?  Because it's much easier to merge a new
+    specifier into an existing collection and then check that the resulting
+    thing is valid, rather than having to deal with all of the possible
+    special cases of what the new thing might be.  And if the merged
+    collection isn't good, I must roll back to the previous version.  So I
+    don't get to take advantage of a mutable structure.)"))
  
  (defparameter *good-declspecs*
    '(((:int) (:signed :unsigned) (:short :long :long-long) ())
                      ((and (eq (ds-label old) :long) (eq ds old))
                       (values t (gethash :long-long *declspec-map*)))
                      (t (values nil nil))))
+         (specs (values t (adjoin (ds-spec ds) old)))
          (t (values (not old) ds)))
        (if ok
          (let ((copy (copy-instance specs)))
  
  (defun declspecs-type (specs)
    "Convert `declspecs' SPECS into a standalone C type object."
-   (let ((type (ds-type specs))
-       (size (ds-size specs))
-       (sign (ds-sign specs))
-       (cplx (ds-complexity specs))
-       (quals (mapcar #'ds-label (ds-qualifiers specs))))
-     (cond ((typep type 'c-type)
-          (qualify-c-type type quals))
-         ((or type size sign cplx)
-          (when (and sign (eq (ds-label sign) :signed)
-                     (eq (ds-label type) :int))
-            (setf sign nil))
-          (cond ((and (or (null type) (eq (ds-label type) :int))
-                      (or size sign))
-                 (setf type nil))
-                ((null type)
-                 (setf type (gethash :int *declspec-map*))))
-          (make-simple-type (format nil "~{~@[~A~^ ~]~}"
-                                    (mapcar #'ds-name
-                                            (remove nil
-                                                    (list sign cplx
-                                                          size type))))
-                            quals))
-         (t
-          nil))))
+   (let* ((base-type (ds-type specs))
+        (size (ds-size specs))
+        (sign (ds-sign specs))
+        (cplx (ds-complexity specs))
+        (quals (mapcar #'ds-label (ds-qualifiers specs)))
+        (specs (ds-specs specs))
+        (type (cond ((typep base-type 'c-type)
+                     (qualify-c-type base-type quals))
+                    ((or base-type size sign cplx)
+                     (when (and sign (eq (ds-label sign) :signed)
+                                (eq (ds-label base-type) :int))
+                       (setf sign nil))
+                     (cond ((and (or (null base-type)
+                                     (eq (ds-label base-type) :int))
+                                 (or size sign))
+                            (setf base-type nil))
+                           ((null base-type)
+                            (setf base-type (gethash :int *declspec-map*))))
+                     (let* ((things (list sign cplx size base-type))
+                            (stripped (remove nil things))
+                            (names (mapcar #'ds-name stripped)))
+                       (make-simple-type (format nil "~{~A~^ ~}" names)
+                                         quals)))
+                    (t
+                     nil))))
+     (cond ((null type) nil)
+         ((null specs) type)
+         (t (make-storage-specifiers-type type specs)))))
  
  ;; Parsing declaration specifiers.
  
  (define-indicator :declspec "<declaration-specifier>")
  
- (defun scan-declspec
+ (defun scan-simple-declspec
      (scanner &key (predicate (constantly t)) (indicator :declspec))
-   "Scan a `declspec' from SCANNER.
+   "Scan a simple `declspec' from SCANNER.
+    Simple declspecs are the ones defined in the `*declspec-map*' or
+    `*module-type-map*'.  This covers the remaining possibilities if the
+    `complex-declspec' pluggable parser didn't find anything to match.
  
     If PREDICATE is provided then only succeed if (funcall PREDICATE DECLSPEC)
     is true, where DECLSPEC is the raw declaration specifier or C-type object,
           (scanner-step scanner)
           (values ds t t)))))
  
+ (define-pluggable-parser complex-declspec atomic-typepsec (scanner)
+   ;; `atomic' `(' type-name `)'
+   ;; `_Atomic' `(' type-name `)'
+   (with-parser-context (token-scanner-context :scanner scanner)
+     (parse (peek (seq ((nil (or "atomic" "_Atomic"))
+                      #\(
+                      (decls (parse-c-type scanner))
+                      (subtype (parse-declarator scanner decls
+                                                 :kernel (parse-empty)
+                                                 :abstractp t))
+                      #\))
+                  (make-atomic-type (car subtype)))))))
+ (define-pluggable-parser complex-declspec alignas (scanner)
+   ;; `alignas' `(' fragment `)'
+   ;; `_Alignas' `(' fragment `)'
+   (with-parser-context (token-scanner-context :scanner scanner)
+     (parse (peek (seq ((nil (or "alignas" "_Alignas"))
+                      (nil (lisp (values #\(
+                                         (eq (token-type scanner) #\()
+                                         nil)))
+                      (nil (commit))
+                      (frag (parse-delimited-fragment scanner #\( #\))))
+                  (make-instance 'storespec
+                                 :spec (make-instance
+                                        'alignas-storage-specifier
+                                        :alignment frag)))))))
  (defun scan-and-merge-declspec (scanner specs)
    "Scan a declaration specifier and merge it with SPECS.
  
     SPECS."
  
    (with-parser-context (token-scanner-context :scanner scanner)
-     (if-parse (:consumedp consumedp) (scan-declspec scanner)
+     (if-parse (:consumedp consumedp)
+             (or (plug complex-declspec scanner)
+                 (scan-simple-declspec scanner))
        (aif (combine-declspec specs it)
           (values it t consumedp)
           (values (list :declspec) nil consumedp)))))
  ;;; `parse-declarator' will be of this form.
  
  (export 'parse-declarator)
 -(defun parse-declarator (scanner base-type &key kernel abstractp)
 +(defun parse-declarator (scanner base-type &key kernel abstractp keywordp)
    "Parse a C declarator, returning a pair (C-TYPE . NAME).
  
     The SCANNER is a token scanner to read from.  The BASE-TYPE is the type
     defaults to matching a simple identifier `:id'.  This might, e.g., be
     (? :id) to parse an `abstract declarator' which has optional names.
  
 +   If KEYWORDP is true, then a keyword argument list is permitted in
 +   function declarations.
 +
     There's an annoying ambiguity in the syntax, if an empty KERNEL is
     permitted.  In this case, you must ensure that ABSTRACTP is true so that
     the appropriate heuristic can be applied.  As a convenience, if ABSTRACTP
     is true then `(? :id)' is used as the default KERNEL."
 +
 +  ;; This is a bit confusing.  This is a strangely-shaped operator grammer,
 +  ;; which wouldn't be so bad, but the `values' being operated on are pairs
 +  ;; of the form (FUNC . NAME).  The NAME is whatever the KERNEL parser
 +  ;; produces as its result, and will be passed out unchanged.  The FUNC is a
 +  ;; type-constructor function which will be eventually be applied to the
 +  ;; input BASE-TYPE, but we can't calculate the actual result as we go along
 +  ;; because of the rather annoying inside-out nature of the declarator
 +  ;; syntax.
 +
    (with-parser-context (token-scanner-context :scanner scanner)
      (let ((kernel-parser (cond (kernel kernel)
                               (abstractp (parser () (? :id)))
  
                 (parse
                   (seq ((quals (list ()
-                                 (scan-declspec
+                                 (scan-simple-declspec
                                   scanner
                                   :indicator :qualifier
                                   :predicate (lambda (ds)
                                                         'qualifier)))))))
                     (mapcar #'ds-label quals))))
  
 +             (disallow-keyword-functions (type)
 +               (when (typep type 'c-keyword-function-type)
 +                 (error "Functions with keyword arguments are only ~
 +                         allowed at top-level.")))
 +
               (star ()
                 ;; Prefix: `*' qualifiers
  
                 (parse (seq (#\* (quals (qualifiers)))
                          (preop "*" (state 9)
                            (cons (lambda (type)
 +                                  (disallow-keyword-functions type)
                                    (funcall (car state)
                                             (make-pointer-type type quals)))
                                  (cdr state))))))
                 (parse (seq ((name (funcall kernel-parser)))
                          (cons #'identity name))))
  
 +             (arg-decl (abstractp)
 +               (parse (seq ((base-type (parse-c-type scanner))
 +                            (dtor (parse-declarator scanner base-type
 +                                                    :abstractp abstractp)))
 +                        dtor)))
 +
 +             (argument ()
 +               ;; argument ::= type abstract-declspec
 +
 +               (parse (seq ((dtor (arg-decl t)))
 +                        (make-argument (cdr dtor) (car dtor)))))
 +
 +             (kw-argument ()
 +               ;; kw-argument ::= type declspec [= c-fragment]
 +
 +               (parse (seq ((dtor (arg-decl nil))
 +                            (dflt (? (when (eq (token-type scanner) #\=)
 +                                       (parse-delimited-fragment
 +                                        scanner #\= '(#\, #\))
 +                                        :keep-end t)))))
 +                        (make-argument (cdr dtor) (car dtor) dflt))))
 +
               (argument-list ()
 -               ;; [argument [`,' argument]* [`,' `...']] | `...'
 +               ;; argument-list ::=
 +               ;;     [argument [`,' argument]* [`,' argument-tail]]
 +               ;;   | argument-tail
 +               ;;
 +               ;; argument-tail ::= `...' | keyword-tail
 +               ;;
 +               ;; keyword-tail ::= `?' [kw-argument [`,' kw-argument]*]
 +               ;;
 +               ;; kw-argument ::= argument [= c-fragment]
                 ;;
                 ;; The possibility of a trailing `,' `...' means that we
                 ;; can't use the standard `list' parser.  Note that, unlike
                 ;; `real' C, we allow an ellipsis even if there are no
                 ;; explicit arguments.
  
 -               (let ((args nil))
 +               (let ((args nil)
 +                     (keys nil)
 +                     (keysp nil))
                   (loop
                     (when (eq (token-type scanner) :ellipsis)
                       (push :ellipsis args)
                       (scanner-step scanner)
                       (return))
 +                   (when (and keywordp (eq (token-type scanner) #\?))
 +                     (setf keysp t)
 +                     (scanner-step scanner)
 +                     (multiple-value-bind (arg winp consumedp)
 +                         (parse (list (:min 0) (kw-argument) #\,))
 +                       (declare (ignore consumedp))
 +                       (unless winp
 +                         (return-from argument-list (values arg nil t)))
 +                       (setf keys arg)
 +                       (return)))
                     (multiple-value-bind (arg winp consumedp)
 -                       (parse (seq ((base-type (parse-c-type scanner))
 -                                    (dtor (parse-declarator scanner
 -                                                            base-type
 -                                                            :abstractp t)))
 -                                (make-argument (cdr dtor) (car dtor))))
 +                       (argument)
                       (unless winp
                         (if (or consumedp args)
                             (return-from argument-list (values arg nil t))
                     (unless (eq (token-type scanner) #\,)
                       (return))
                     (scanner-step scanner))
 -                 (values (nreverse args) t args)))
 +                 (values (let ((rargs (nreverse args))
 +                               (rkeys (nreverse keys)))
 +                           (if keysp
 +                               (lambda (ret)
 +                                 (make-keyword-function-type
 +                                  ret rargs rkeys))
 +                               (lambda (ret)
 +                                 (make-function-type ret rargs))))
 +                         t
 +                         (or args keysp))))
  
               (postfix-lparen ()
                 ;; Postfix: `(' argument-list `)'
  
 -               (parse (seq (#\( (args (argument-list)) #\))
 +               (parse (seq (#\( (make (argument-list)) #\))
                          (postop "()" (state 10)
                            (cons (lambda (type)
 +                                  (disallow-keyword-functions type)
                                    (funcall (car state)
 -                                           (make-function-type type args)))
 +                                           (funcall make type)))
                                  (cdr state))))))
  
               (dimension ()
                 (parse (seq ((dims (list (:min 1) (dimension))))
                          (postop "[]" (state 10)
                            (cons (lambda (type)
 +                                  (disallow-keyword-functions type)
                                    (funcall (car state)
                                             (make-array-type type dims)))
                                  (cdr state)))))))
                              (or (postfix-lparen)
                                  (lbracket)
                                  (when nestedp (seq (#\)) (rparen #\))))))))
-                (cons (funcall (car value) base-type) (cdr value))))))))
+                (cons (wrap-c-type (lambda (type)
+                                     (funcall (car value) type))
+                                   base-type)
+                      (cdr value))))))))
  
  ;;;----- That's all, folks --------------------------------------------------
diff --combined src/c-types-proto.lisp
index 9fe61267cb4d0b09a61744d4b38a2ada711a5882,0c95d024d0d667219d2bd08ba8711f7e9171ccbb..f8e758999b216e91442ead0d806e7f085508d98b
     The qualifiers of the returned type are the union of the requested
     QUALIFIERS and the qualifiers already applied to TYPE."))
  
+ (export 'c-qualifier-keyword)
+ (defgeneric c-qualifier-keyword (qualifier)
+   (:documentation "Return the C keyword for the QUALIFIER (a Lisp keyword).")
+   (:method ((qualifier symbol)) (string-downcase qualifier)))
+ (export 'c-type-qualifier-keywords)
+ (defun c-type-qualifier-keywords (c-type)
+   "Return the type's qualifiers, as a list of C keyword names."
+   (mapcar #'c-qualifier-keyword (c-type-qualifiers c-type)))
  (export 'c-type-subtype)
  (defgeneric c-type-subtype (type)
    (:documentation
  (export '(expand-c-type-spec expand-c-type-form))
  (eval-when (:compile-toplevel :load-toplevel :execute)
    (defgeneric expand-c-type-spec (spec)
-     (:documentation
-      "Expand SPEC into Lisp code to construct a C type.")
+     (:documentation "Expand SPEC into Lisp code to construct a C type.")
      (:method ((spec list))
        (expand-c-type-form (car spec) (cdr spec))))
    (defgeneric expand-c-type-form (head tail)
-     (:documentation
-      "Expand a C type list beginning with HEAD.")
+     (:documentation "Expand a C type list beginning with HEAD.")
      (:method ((name (eql 'lisp)) tail)
        `(progn ,@tail))))
  
    (expand-c-type-spec spec))
  
  (export 'define-c-type-syntax)
- (defmacro define-c-type-syntax (name bvl &rest body)
+ (defmacro define-c-type-syntax (name bvl &body body)
    "Define a C-type syntax function.
  
     A function defined by BODY and with lambda-list BVL is associated with the
-    NAME.  When `expand-c-type' sees a list (NAME . STUFF), it will call this
-    function with the argument list STUFF."
+    NAME.  When `expand-c-type-spec' sees a list (NAME . STUFF), it will call
+    this function with the argument list STUFF."
    (with-gensyms (head tail)
      (multiple-value-bind (doc decls body) (parse-body body)
        `(eval-when (:compile-toplevel :load-toplevel :execute)
    "Define NAMES all to describe the C-type VALUE.
  
     NAMES can be a symbol (treated as a singleton list), or a list of symbols.
-    The VALUE is a C type S-expression, acceptable to `expand-c-type'.  It
-    will be expanded once at run-time."
+    The VALUE is a C type S-expression, acceptable to `expand-c-type-spec'.
+    It will be expanded once at run-time."
    (let* ((names (if (listp names) names (list names)))
         (namevar (gensym "NAME"))
         (typevar (symbolicate 'c-type- (car names))))
                              (error "Bad character in C name ~S." name))))))
      (t name)))
  
+ ;;;--------------------------------------------------------------------------
+ ;;; Storage specifier protocol.
+ (export 'pprint-c-storage-specifier)
+ (defgeneric pprint-c-storage-specifier (spec stream)
+   (:documentation "Print the storage specifier SPEC to STREAM, as C syntax.")
+   (:method ((spec symbol) stream) (princ (string-downcase spec) stream)))
+ (export 'print-c-storage-specifier)
+ (defgeneric print-c-storage-specifier (stream spec &optional colon atsign)
+   (:documentation
+    "Print the storage specifier SPEC to STREAM, as an S-expression.
+    This function is suitable for use in `format's ~/.../ command.")
+   (:method (stream (spec t) &optional colon atsign)
+     (declare (ignore colon atsign))
+     (prin1 spec stream))
+   (:method (stream (spec symbol) &optional colon atsign)
+     (declare (ignore colon atsign))
+     (princ (string-downcase spec) stream)))
+ (export '(expand-c-storage-specifier expand-c-storage-specifier-form))
+ (eval-when (:compile-toplevel :load-toplevel :execute)
+   (defgeneric expand-c-storage-specifier (spec)
+     (:documentation
+      "Expand SPEC into Lisp code to construct a storage specifier.")
+     (:method ((spec list))
+       (expand-c-storage-specifier-form (car spec) (cdr spec)))
+     (:method ((spec symbol))
+       (if (keywordp spec) spec
+         (expand-c-storage-specifier-form spec nil))))
+   (defgeneric expand-c-storage-specifier-form (head tail)
+     (:documentation
+      "Expand a C storage-specifier form beginning with HEAD.")
+     (:method ((name (eql 'lisp)) tail)
+       `(progn ,@tail))))
+ (export 'define-c-storage-specifier-syntax)
+ (defmacro define-c-storage-specifier-syntax (name bvl &body body)
+   "Define a C storage-specifier syntax function.
+    A function defined by BODY and with lambda-list BVL is associated wth the
+    NAME.  When `expand-c-storage-specifier' sees a list (NAME . STUFF), it
+    will call this function with the argument list STUFF."
+   (with-gensyms (head tail)
+     (multiple-value-bind (doc decls body) (parse-body body)
+       `(eval-when (:compile-toplevel :load-toplevel :execute)
+        (defmethod expand-c-storage-specifier-form
+            ((,head (eql ',name)) ,tail)
+          ,@doc
+          (destructuring-bind ,bvl ,tail
+            ,@decls
+            (block ,name ,@body)))
+        ',name))))
+ ;;;--------------------------------------------------------------------------
+ ;;; A type for carrying storage specifiers.
+ (export '(c-storage-specifiers-type c-type-specifiers))
+ (defclass c-storage-specifiers-type (c-type)
+   ((specifiers :initarg :specifiers :type list :reader c-type-specifiers)
+    (subtype :initarg :subtype :type c-type :reader c-type-subtype))
+   (:documentation
+    "A type for carrying storage specifiers.
+    Properly, storage specifiers should only appear on an outermost type.
+    This fake C type is a handy marker for the presence of storage specifiers,
+    so that they can be hoisted properly when constructing derived types."))
+ (export 'wrap-c-type)
+ (defun wrap-c-type (wrapper-func base-type)
+   "Handle storage specifiers correctly when making a derived type.
+    WRAPPER-FUNC should be a function which will return some derived type of
+    BASE-TYPE.  This function differs from `funcall' only when BASE-TYPE is
+    actually a `c-storage-specifiers-type', in which case it invokes
+    WRAPPER-FUNC on the underlying type, and re-attaches the storage
+    specifiers to the derived type."
+   (if (typep base-type 'c-storage-specifiers-type)
+       (let* ((unwrapped-type (c-type-subtype base-type))
+            (wrapped-type (funcall wrapper-func unwrapped-type))
+            (specifiers (c-type-specifiers base-type)))
+       (make-or-intern-c-type 'c-storage-specifiers-type unwrapped-type
+                              :specifiers specifiers
+                              :subtype wrapped-type))
+       (funcall wrapper-func base-type)))
  ;;;--------------------------------------------------------------------------
  ;;; Function arguments.
  
 -(export '(argument argumentp make-argument argument-name argument-type))
 -(defstruct (argument (:constructor make-argument (name type
 +(export '(argument argumentp make-argument
 +        argument-name argument-type argument-default))
 +(defstruct (argument (:constructor make-argument (name type &optional default
                                                  &aux (%type type)))
                     (:predicate argumentp))
    "Simple structure representing a function argument."
    (name nil :type t :read-only t)
 -  (%type nil :type c-type :read-only t))
 +  (%type nil :type c-type :read-only t)
 +  (default nil :type t :read-only t))
  (define-access-wrapper argument-type argument-%type :read-only t)
  
  (export 'commentify-argument-name)
diff --combined src/module-parse.lisp
index 35e07ea791a5f2b086d57501ec8f652ccb427d98,e33b6d3bc3ba342969f838b4e3769a334d57e71b..15a7d8f8ffd78e8fde8db41dc73e55c963486339
  
  (export 'class-item)
  
 +(define-pluggable-parser class-item initfrags (scanner class pset)
 +  ;; raw-class-item ::= frag-keyword `{' c-fragment `}'
 +  ;; frag-keyword ::= `init' | `teardown'
 +  (with-parser-context (token-scanner-context :scanner scanner)
 +    (parse (seq ((make (or (seq ("init") #'make-sod-class-initfrag)
 +                         (seq ("teardown") #'make-sod-class-tearfrag)))
 +               (frag (parse-delimited-fragment scanner #\{ #\})))
 +           (funcall make class frag pset scanner)))))
 +
 +(define-pluggable-parser class-item initargs (scanner class pset)
 +  ;; initarg-item ::= `initarg' declspec+ init-declarator-list
 +  ;; init-declarator ::= declarator [`=' initializer]
 +  (with-parser-context (token-scanner-context :scanner scanner)
 +    (parse (seq ("initarg"
 +               (base-type (parse-c-type scanner))
 +               (nil (skip-many (:min 1)
 +                      (seq ((declarator (parse-declarator scanner
 +                                                          base-type))
 +                            (init (? (parse-delimited-fragment
 +                                      scanner #\= (list #\; #\,)
 +                                      :keep-end t))))
 +                        (make-sod-user-initarg class
 +                                               (cdr declarator)
 +                                               (car declarator)
 +                                               pset init scanner))
 +                      #\,))
 +                 #\;)))))
 +
  (defun parse-class-body (scanner pset name supers)
    ;; class-body ::= `{' class-item* `}'
    ;;
                 ;; names.
                 (parse-declarator
                  scanner base-type
 +                :keywordp t
                  :kernel (parser ()
                            (seq ((name-a :id)
                                  (name-b (? (seq (#\. (id :id)) id))))
                                           body sub-pset scanner))))
  
               (parse-initializer ()
 -               ;; initializer ::= `=' c-fragment | `=' `{' c-fragment `}'
 +               ;; initializer ::= `=' c-fragment
                 ;;
 -               ;; Return (VALUE-KIND . VALUE-FORM), ready for passing to a
 -               ;; `sod-initializer' constructor.
 -
 -               ;; This is kind of tricky because we have to juggle both
 -               ;; layers of the parsing machinery.  The character scanner
 -               ;; will already have consumed the lookahead token (which, if
 -               ;; we're going to do anything, is `=').
 -               (let ((char-scanner (token-scanner-char-scanner scanner)))
 -
 -                 ;; First, skip the character-scanner past any whitespace.
 -                 ;; We don't record this consumption, which is a bit
 -                 ;; naughty, but nobody will actually mind.
 -                 (loop
 -                   (when (or (scanner-at-eof-p char-scanner)
 -                             (not (whitespace-char-p
 -                                   (scanner-current-char char-scanner))))
 -                     (return))
 -                   (scanner-step char-scanner))
 -
 -                 ;; Now maybe read an initializer.
 -                 (cond ((not (eql (token-type scanner) #\=))
 -                        ;; It's not an `=' after all.  There's no
 -                        ;; initializer.
 -                        (values '(#\=) nil nil))
 -
 -                       ((and (not (scanner-at-eof-p char-scanner))
 -                             (char= (scanner-current-char char-scanner)
 -                                    #\{))
 -                        ;; There's a brace after the `=', so we should
 -                        ;; consume the `=' here, and read a compound
 -                        ;; initializer enclosed in braces.
 -                        (parse (seq (#\= (frag (parse-delimited-fragment
 -                                                scanner #\{ #\})))
 -                                 (cons :compound frag))))
 -
 -                       (t
 -                        ;; No brace, so read from the `=' up to, but not
 -                        ;; including, the trailing `,' or `;' delimiter.
 -                        (parse (seq ((frag (parse-delimited-fragment
 -                                            scanner #\= '(#\; #\,)
 -                                            :keep-end t)))
 -                                 (cons :simple frag)))))))
 +               ;; Return a VALUE, ready for passing to a `sod-initializer'
 +               ;; constructor.
 +               (parse-delimited-fragment scanner #\= (list #\, #\;)
 +                                         :keep-end t))
  
               (parse-slot-item (sub-pset base-type type name)
                 ;; slot-item ::=
                                              sub-pset scanner)
                               (when init
                                 (make-sod-instance-initializer
 -                                class nick name (car init) (cdr init)
 -                                sub-pset scanner)))
 +                                class nick name init sub-pset scanner)))
                             (skip-many ()
                               (seq (#\,
                                     (ds (parse-declarator scanner
                                                sub-pset scanner)
                                 (when init
                                   (make-sod-instance-initializer
 -                                  class nick (cdr ds)
 -                                  (car init) (cdr init)
 +                                  class nick (cdr ds) init
                                    sub-pset scanner))))
                             #\;)))
  
 -             (parse-initializer-item (sub-pset constructor)
 +             (parse-initializer-item (sub-pset must-init-p constructor)
                 ;; initializer-item ::=
                 ;;     [`class'] -!- slot-initializer-list `;'
                 ;;
 -               ;; slot-initializer ::= id `.' id initializer
 -               (parse (and (skip-many ()
 -                             (seq ((name-a :id) #\. (name-b :id)
 -                                   (init (parse-initializer)))
 -                               (funcall constructor class
 -                                        name-a name-b
 -                                        (car init) (cdr init)
 -                                        sub-pset scanner))
 -                             #\,)
 -                           #\;)))
 +               ;; slot-initializer ::= id `.' id [initializer]
 +               (let ((parse-init (if must-init-p
 +                                     #'parse-initializer
 +                                     (parser () (? (parse-initializer))))))
 +                 (parse (and (skip-many ()
 +                               (seq ((name-a :id) #\. (name-b :id)
 +                                     (init (funcall parse-init)))
 +                                 (funcall constructor class
 +                                          name-a name-b init
 +                                          sub-pset scanner))
 +                               #\,)
 +                             #\;))))
  
               (class-item-dispatch (sub-pset base-type type name)
                 ;; Logically part of `parse-raw-class-item', but the
                 ;;   | method-item
                 ;;   | slot-item
                 ;;   | initializer-item
 +               ;;   | initfrag-item
                 ;;
                 ;; Most of the above begin with declspecs and a declarator
                 ;; (which might be dotted).  So we parse that here and
                            (peek
                             (seq ((ds (parse-c-type scanner))
                                   (dc (parse-maybe-dotted-declarator ds))
+                                  (nil (commit))
                                   (nil (class-item-dispatch sub-pset
                                                             ds
                                                             (car dc)
                                                             (cdr dc))))))
                            (and "class"
                                 (parse-initializer-item
 -                                sub-pset
 +                                sub-pset t
                                  #'make-sod-class-initializer))
                            (parse-initializer-item
 -                           sub-pset
 +                           sub-pset nil
                             #'make-sod-instance-initializer)))))
  
        (parse (seq (#\{
diff --combined src/sod-module.5
index 66937dacc910d4b0b96c1d2f34e9dd47156ed949,18375246943fa78275580323691a2b5c76af1c05..949e781593dfb0d4f93f654a131b179dc17f4e1c
@@@ -542,10 -542,6 +542,10 @@@ class-definitio
  .|
  .I initializer-item
  .|
 +.I initarg-item
 +.|
 +.I fragment-item
 +.|
  .I message-item
  .|
  .I method-item
  .I slot-initializer
  ::=
  .I dotted-name
 -.B =
 -.I initializer
 +.RB [ =
 +.IR initializer ]
  .br
  .I initializer
  ::=
 +.I c-fragment
 +.br
 +.I initarg-item
 +::=
 +.<
 +.B initarg
 +.IR declaration-specifier \*+
 +.IR list [ init-declarator ]
 +.B ;
 +.br
 +.I fragment-item
 +::=
 +.I fragment-kind
  .B {
  .I c-fragment
  .B }
 +.br
 +.I fragment-kind
 +::=
 +.B init
  |
 -.I c-fragment
 +.B teardown
  .br
  .I message-item
  ::=
  .<
  .IR declaration-specifier \*+
 -.I simple-declarator
 +.IR keyword-declarator [ identifier ]
 +.<
  .RI [ method-body ]
  .br
  .I method-item
  ::=
  .<
  .IR declaration-specifier \*+
 -.IR declarator [ dotted-name ]
 +.IR keyword-declarator [ dotted-name ]
 +.<
  .I method-body
  .br
  .I method-body
  .B _Complex
  .|
  .I qualifier
+ .|
+ .I storage-specifier
+ .|
+ .I atomic-type
  .br
  .I qualifier
  ::=
+ .I atomic
+ |
  .B const
  |
  .B volatile
  |
  .B restrict
  .br
+ .I atomic-type
+ ::=
+ .I
+ atomic
+ .B (
+ .IR declaration-specifier \*+
+ .I abstract-declarator
+ .B )
+ .br
+ .I atomic
+ ::=
+ .B atomic
+ |
+ .B _Atomic
+ .br
+ .I storage-specifier
+ ::=
+ .I alignas
+ .B (
+ .I c-fragment
+ .B )
+ .br
+ .I alignas
+ ::=
+ .B alignas
+ |
+ .B _Alignas
+ .br
  .I type-name
  ::=
  .I identifier
@@@ -765,6 -776,8 +799,8 @@@ However, not all combinations are permi
  A declaration specifier must consist of 
  zero or more
  .IR qualifier s,
+ zero or more
+ .IR storage-specifier s,
  and one of the following, up to reordering.
  .hP \*o
  .I type-name
  .BR "double complex" ,
  .B "long double complex"
  .PP
 -.IR declarator [ k ]
 +.IR declarator [ k ", " a ]
  ::=
  .IR pointer \**
 -.IR primary-declarator [ k ]
 +.IR primary-declarator [ k ", " a ]
  .br
 -.IR primary-declarator [ k ]
 +.IR primary-declarator [ k ", " a ]
  ::=
  .I k
  .|
  .B (
 -.IR primary-declarator [ k ]
 +.IR primary-declarator [ k ", " a ]
  .B )
  .|
 -.IR primary-declarator [ k ]
 -.IR declarator-suffix
 +.IR primary-declarator [ k ", " a ]
 +.IR declarator-suffix [ a ]
  .br
  .I pointer
  ::=
  .B *
  .IR qualifier \**
  .br
 -.I declarator-suffix
 +.IR declarator-suffix [ a ]
  ::=
  .B [
  .I c-fragment
  .B ]
  .|
  .B (
 -.I argument-list
 +.I a
  .B )
  .br
  .I argument-list
  .IR declaration-specifier \*+
  .I argument-declarator
  .br
 -.IR declarator [\*e]
+ .I abstract-declarator
+ ::=
++.IR declarator "[\*e, " argument-list ]
+ .br
  .I argument-declarator
  ::=
 -.IR declarator [ identifier " | \*e]"
 +.IR declarator [ identifier " | \*e, " argument-list ]
  .br
  .I simple-declarator
  ::=
 -.IR declarator [ identifier ]
 +.IR declarator [ identifier ", " argument-list ]
 +.br
 +.I keyword-argument
 +::=
 +.I argument
 +.RB [ =
 +.IR c-fragment ]
 +.br
 +.I keyword-argument-list
 +::=
 +.I argument-list
 +.B ?\&
 +.I keyword-argument-list
 +.br
 +.I method-argument-list
 +::=
 +.I argument-list
 +|
 +.I keyword-argument-list
  .br
  .I dotted-name
  ::=
  .I identifier
  .B .\&
  .I identifier
 +.br
 +.IR keyword-declarator [ k ]
 +::=
 +.IR declarator [ k ", " method-argument-list ]
  .
  .\"--------------------------------------------------------------------------
  .SH SEE ALSO