0.5.0: an island's entry points arrive in the module's own namespace - #13
Merged
Conversation
`docs/42` states one rule for both lanes -- the module name and the namespace are one identifier path -- and the shader lane follows it while this generator did not. Every entry point was emitted at global scope, so `import app.kernels` bought a file name and nothing else, and a project with device code in several directories got no help from the names it reached that code through. The names now follow the rule. A root's directories extend the namespace exactly as a payload tree's do, and the C++ side reaches `app::kernels::image::blur` through one import and one flat header. A namespace over a flat symbol is a lookup alias. Measured with clang++ (DPC++ 7.1.0), -std=c++23: two modules re-exporting one `extern "C"` name into two namespaces produce two spellings of one entity, `&a::f == &b::f`. The shader lane does not have this problem because it composes its own symbols; an island's symbol is written by its author and this generator only reads it. So the namespaces are admissible only together with a refusal: one name declared twice in one root is a collision, named with both files. A name then exists in exactly one namespace and cannot lie about what a call resolves to. The API says roots rather than files. `options::roots` names the directories implementations live under and `options::layout_root` names the one whose structure decides where entry points live. A file list's common ancestor moves when a file is added, and a consumer's qualified name would move with it; a root taken from `mcpp::device_sources()` would vanish under `--no-accel` and the namespace would come from the fallback tree instead. Every other root only has to define the names, so a fallback may be one flat file and may be reorganised without renaming anything a consumer wrote. Two drafts were discarded and are recorded in the design: requiring every implementation to sit at the same relative directory, which refused an ordinary flat fallback; and taking the deepest directory among them, which let a refactor of a tree nobody consumes rename what every consumer writes. `options::strip_prefix` emits a short spelling beside the authored name, which stays canonical: an island's symbol is global to the whole program, so an entry point carries a prefix the namespace then repeats. A `constexpr` function pointer costs the artifact nothing -- the pair is one symbol, asserted with nm. `common_base_dir` and `namespace_of` had been written in `rules/spirv.cppm` and again in `rules/slang.cppm`. They move to `mcpp::plugins::names` with the identifier sanitiser, so a directory named `default` or `2d` gets one answer rather than two that agree by inspection. Also: a scan that finds no marked entry point is an error naming the roots rather than a module exporting nothing; the walk is ordered, so the generated files are a function of the tree and not of the filesystem's enumeration; and each root is registered as a glob, so adding a file re-runs the program. The floor does not move. This changes what the package generates, not what it asks the engine for.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Implements
.agents/docs/2026-09-08-island-boundary-names.mdin this repository. The mcpp pull request that updates the documentation and the examples follows this one, becauseexamples/09-heterogeneous/boundaryresolves this package through the index.What was wrong
docs/42states one rule for both lanes: the module name and the namespace are one identifier path. The shader lane follows it --myapp.shadersgivesmyapp::shaders::post::tonemap_frag(). This generator did not: every entry point was emitted at global scope, soimport app.kernelsbought a file name and nothing else, and a project with device code in several directories got no help from the names it reached that code through.What changed
A root's directories extend the namespace exactly as a payload tree's do:
One import, one flat header. The header keeps no namespace: it is read by a C or a device compiler, and neither has one to read.
The check is what makes the namespace honest. A namespace over a flat symbol is a lookup alias. Measured with clang++ (DPC++ 7.1.0),
-std=c++23: two modules re-exporting oneextern "C"name into two namespaces produce two spellings of one entity,&a::f == &b::f. The shader lane escapes this because it composes its own symbols; an island's symbol is written by its author and this generator only reads it. So one name declared twice in one root is refused, naming both files. A name then exists in exactly one namespace.Roots, not a file list.
options::rootsnames the directories implementations live under;options::layout_rootnames the one whose structure decides where entry points live and defaults to the first. A file list's common ancestor moves when a file is added, and a consumer's qualified name would move with it. Roots must also not come frommcpp::device_sources(), whichaccelnarrows to nothing under--no-accel.Every other root only has to define the names, so a fallback tree may be one flat file and may be reorganised without renaming anything a consumer wrote. This is a naming role and not a rank: every root compiles, links and is equally a backend.
options::strip_prefixemits a short spelling beside the authored name, which stays canonical. Aconstexprfunction pointer costs the artifact nothing; the pair is one symbol, asserted withnm.One sanitiser.
common_base_dirandnamespace_ofhad been written inrules/spirv.cppmand again inrules/slang.cppm. They move tomcpp::plugins::nameswith the identifier sanitiser.Criteria
The fixture's shape is itself a criterion:
src/kernelsis the layout root and holds a subdirectory, andsrc/cpuimplements the same two entry points in one flat file. Nine steps, each with a denominator:image/scale.clands in::kernelsnamespacein a header no C compiler can read.cppmdiffers between the two legsAll nine were run locally against the released mcpp 2026.9.8.1 before this was opened, together with
every rule module compiles for this host,rules-spirv through the module surface,rules-slang through a consumerandtools-embed through the module surfacefor the shared-derivation move.Compatibility
Breaking, deliberately and without a flag. A consumer writes
app::kernels::saxpy_device(...)where it wrotesaxpy_device(...).tools-islandshipped 2026.9.7.1 and the call sites are the three heterogeneous examples in mcpp and this fixture. A flag that kept the global spelling alive would leave the namespace decorative, which is the outcome the design exists to avoid.The floor does not move: this changes what the package generates, not what it asks the engine for.