| 1 | cfa-cppp - CForall pre-preprocessor
 | 
|---|
| 2 | 
 | 
|---|
| 3 | POC deriving header files from a compile unit's source.
 | 
|---|
| 4 | 
 | 
|---|
| 5 | Input: Aet of interdependent *.src.c files, each written as if header files are not a consideration (Java-style, definitions only), annotated with both the language additions and POC scaffolding listed below.
 | 
|---|
| 6 | 
 | 
|---|
| 7 | Key Intermediate: For each %.src.c input, %.tdcl.h, %.defn.h, %.impl.c, containing relevant true-cpp #include directives of each other, such that the resulting classic-C build of a self-sufficient set of *.impl.c files gives the demo's Output.
 | 
|---|
| 8 | 
 | 
|---|
| 9 | Output: Linked and running program
 | 
|---|
| 10 | 
 | 
|---|
| 11 | Terminology
 | 
|---|
| 12 | - auto: as in C, "exported from here;" different from `extern`, which means "exported by someone else" in C
 | 
|---|
| 13 | - shred: "Shredding foo.src.c" means producing the "foo" Key Intermediates from the "foo" Input.
 | 
|---|
| 14 | - value: the opposite of a type, includes function
 | 
|---|
| 15 | - vicious: cyclic dependency that cannot be bootstrapped with the tools under POC
 | 
|---|
| 16 | 
 | 
|---|
| 17 | 
 | 
|---|
| 18 | To run:
 | 
|---|
| 19 |     if grep -q 'cfa-cppp' Makefile; then echo ok; else echo Wrong folder; fi;
 | 
|---|
| 20 |     make                                    # expect success
 | 
|---|
| 21 |     hello/a.out                             # expect log of fcn calls and glb-var vals
 | 
|---|
| 22 |     coop/a.out                              # expect log of chickens hatching from eggs
 | 
|---|
| 23 |     akwd-val-trans/a.out                    # expect four coop-like logs of a/b calls
 | 
|---|
| 24 |     make err-vicious/a.out                  # expect failure after shredding done
 | 
|---|
| 25 |     make clean                              # expect success
 | 
|---|
| 26 |     make hello/a.out CFLAGS=-DERR1          # expect failure after shredding done
 | 
|---|
| 27 |     grep -rE 'ERR[0-9]*' --include=*.src.c  # repeat prev -DERR act / do "manual" steps
 | 
|---|
| 28 |                                             # on resulting grep hits
 | 
|---|
| 29 | 
 | 
|---|
| 30 | 
 | 
|---|
| 31 | Demos are
 | 
|---|
| 32 | - (`err-` means "build is expected to fail")
 | 
|---|
| 33 | - hello: valid hello-world scenario with transitive dependency
 | 
|---|
| 34 |   - linear (bottom-up) build order would be fine
 | 
|---|
| 35 | - coop (chickens and eggs): valid circular dependency, resolved with `import auto &`
 | 
|---|
| 36 |   - "as much mutual recursion as I can cram, without going vicious"
 | 
|---|
| 37 | - vicious: the bridge too far, that coop resisted crossing, a:tdefs <-> b:tdefs
 | 
|---|
| 38 |   - requires an out-of-scope user's recourse, like multiple manual header fragments
 | 
|---|
| 39 |   - there does not exist a C-valid order of each module's offerings obtainable by ordering based on only offering sort (e.g. type definition, value declaration) and compile unit
 | 
|---|
| 40 |   - would never arise as real C code, without a split like one compile unit implementing two headers
 | 
|---|
| 41 |   - key difficulty: each side has a type definition that embeds a type defined on the opposite side
 | 
|---|
| 42 | - typeof: valid cicular dependency, its poential hiding in typeof
 | 
|---|
| 43 |   - b:vdefs -> a:tdefs -> b:vdecls
 | 
|---|
| 44 | - vicious-typeof: a vicious-cycle potential hiding in typeof, a:vdecls <-> b:vdecls
 | 
|---|
| 45 |   - comments under 'vicious' apply
 | 
|---|
| 46 |   - (including) expect recourse like multiple manual header fragments to resolve
 | 
|---|
| 47 | - vicious*/recourse-classic
 | 
|---|
| 48 |   - example of a manual header split that resolves the circularity
 | 
|---|
| 49 | - vicious*/recourse-proposed
 | 
|---|
| 50 |   - same split, given in a plausible headerless representation that's not demo-implemented
 | 
|---|
| 51 | 
 | 
|---|
| 52 | In scope
 | 
|---|
| 53 | - module-level export vs private
 | 
|---|
| 54 | - automatically handle circular dependency  (demos: valid=coop invalid=err-vicious)
 | 
|---|
| 55 | - ordered dependency with transitive "re-export" (demo: hello)
 | 
|---|
| 56 | - separate compilation
 | 
|---|
| 57 | - structs, functions and global variables
 | 
|---|
| 58 | 
 | 
|---|
| 59 | Out of Scope
 | 
|---|
| 60 | - (though believed not deal breakers for this proposal)
 | 
|---|
| 61 | - quality error messages
 | 
|---|
| 62 | - private struct fields, friends
 | 
|---|
| 63 | - illustrating how to set up a build (current Makefile is "get it to work")
 | 
|---|
| 64 | - crawling imports ("first-time gcc -MMD"), determining a build order
 | 
|---|
| 65 | - interaction of these modules with preexsting CFA features (current demos are C)
 | 
|---|
| 66 | - extracting shred-relevant information from C source (mocked up with scaffolding below)
 | 
|---|
| 67 | - C preprocessor integration: exporting a macro, ifdef'ing a #import
 | 
|---|
| 68 | - user's recourse for situations where headers are not inferrable
 | 
|---|
| 69 | - typedefs
 | 
|---|
| 70 | - controling visibility of types (equiv. choosing to put a struct in *.h vs *.c)
 | 
|---|
| 71 | - proffering implementation (equiv. function bodies in the header, like for static-inline)
 | 
|---|
| 72 | - rejecting attempt to offer something public that can't be understood without seeing something private
 | 
|---|
| 73 | - relaxing declare before use
 | 
|---|
| 74 | - legacy integration (demo uses printf/exit as loose declarations, main as "just define it")
 | 
|---|
| 75 | - linker control (demo dumps all exports into one linker namespace)
 | 
|---|
| 76 | 
 | 
|---|
| 77 | 
 | 
|---|
| 78 | Language additions modelled
 | 
|---|
| 79 | - (The proposal is to add nicer versions of these placeholders to CFA)
 | 
|---|
| 80 | - module imports (#import), where `foo` corresponds with foo.c; one of:
 | 
|---|
| 81 | - `#import static foo`: this implementation depends on foo's interface
 | 
|---|
| 82 |   - most common, equivaluent of #include in *.c
 | 
|---|
| 83 | - `#import auto & foo`: above, plus this interface mentions types from foo's interface without relying on size information
 | 
|---|
| 84 |   - relevant limitation is you can't nest those types inside types defined here
 | 
|---|
| 85 |   - it's a recourse for breaking a "normal" include cycle
 | 
|---|
| 86 |   - syntax parallels `forall T &` emphasizing you get enough to define functions that take imported types by reference, but not by value
 | 
|---|
| 87 |   - uncommon, equiv. replacing #include with a type forward declaration or giving *.fwd.h)
 | 
|---|
| 88 | - `#import auto foo`: above, plus this interface has unlimited access to foo's interface (quite common, equiv. #include in *.h)
 | 
|---|
| 89 | 
 | 
|---|
| 90 | Scaffolding for the POC
 | 
|---|
| 91 | - (The proposal is to have better compile-time analysis, making these elements unnecessary)
 | 
|---|
| 92 | - No single-line definitions allowed
 | 
|---|
| 93 | - The first line of every user-given definition is a valid declaration, with its semicolon removed and an open-curly added
 | 
|---|
| 94 | - Every user-given definition is preceded, on the adjacent line, by a `//#` directive, which is the appropriate one of:
 | 
|---|
| 95 |   - `@` for an exported type
 | 
|---|
| 96 |   - `$f` for an exported function
 | 
|---|
| 97 |   - `$v` for an exported variable
 | 
|---|
| 98 |   - `-` for anything static
 | 
|---|
| 99 | - Generally, abide with the shredder being brittle on whitespace
 | 
|---|
| 100 | 
 | 
|---|
| 101 | Choice: semantics of transitive import
 | 
|---|
| 102 | - Option A: To import+export a depended-upon module means re-exporting both its types and its values (functions, variables)
 | 
|---|
| 103 |   - Benefit: the typical basic application of C headers works this way
 | 
|---|
| 104 | - Option B: To import+export a depended-upon module means re-exporting only its types
 | 
|---|
| 105 |   - I'll use those types types in my exported declarations
 | 
|---|
| 106 |   - by importing me, you'll get those types so that you can use my declarations
 | 
|---|
| 107 |   - if you also want its values, you'll need to import them yourself
 | 
|---|
| 108 |   - Benefit: gives importers a tidier symbol table; you get implicitly only what you actually need
 | 
|---|
| 109 | - Incomplete Option A used in demo:  You re-export values only with `import auto M`, but not with `import auto & M`.
 | 
|---|
| 110 |   - This association is unnecessary mental complexity for a user.
 | 
|---|
| 111 |   - To do either a clean option A or B requires
 | 
|---|
| 112 |     - adding a third header flavour (a further split of .defn.h)
 | 
|---|
| 113 |     - furthermore, option A probably needs the rules for desugaring #import into #include to become transitive
 | 
|---|
| 114 |   - Left KISS / "common denominator" here
 | 
|---|
| 115 |   - Present-state workaround to achieve full Option A: Extra import in demo `akwd-val-trans/over_a.src.c`
 | 
|---|
| 116 |   - No option-B mockup available from present state because decision "`import auto M` re-exports values" means present state does exports that option B does not want
 | 
|---|
| 117 | 
 | 
|---|
| 118 | Eventual implementation remark
 | 
|---|
| 119 | - To extract shred-relevant information from C source...
 | 
|---|
| 120 | - For %.c -> %.tdcl.h
 | 
|---|
| 121 |   - Ignore imports
 | 
|---|
| 122 |   - Need a limp-mode parser that does not differentiate types from identifiers
 | 
|---|
| 123 |   - It only has to produce the list of type names (non-recursively) exported, i.e. infer the `//# @` annotations
 | 
|---|
| 124 | - For %.c -> {%.defn.h, %.impl.c}
 | 
|---|
| 125 |   - On seeing `import auto foo` (or `import auto & foo`), pre-load the parser's type/id table as if `import auto & foo`, i.e. recursively consult imports, in limp mode, stopping upon a cycle (like #pragma once).
 | 
|---|
| 126 |   - It's not necessary to know information in foo.defn.h to shred % accurately.
 | 
|---|
| 127 |   - Today's CFA parser works and is sufficient (though overkill)
 | 
|---|
| 128 |   - Parsing here only needs to enable extracting a declaration from its definition.
 | 
|---|
| 129 |   - User errors, like, "You tried to pass an incomplete type by value," can come out later, while compiling %.impl.c.
 | 
|---|