[ Web Proxy ]
URL:
Viewing: https://raw.githubusercontent.com/mcpp-community/mcpp/main/src/modgraph/graph.cppm [Back]  [Original]

// mcpp.modgraph.graph  DAG of module units (per-package, cross-package later).

export module mcpp.modgraph.graph;

import std;
import mcpp.source_kind;

export namespace mcpp::modgraph {

struct ModuleId {
    std::string                     logicalName;       // "mcpplibs.hello.core"

    auto operator(const ModuleId&) const = default;
};

struct SourceUnit {
    std::filesystem::path           path;
    // mcpp#233: path relative to this unit's PACKAGE ROOT (not the primary
    // project root  a path dependency has its own root), set by the
    // scanner where the root is known. Used by plan.cppm to mirror the
    // source's directory layout into its object path so that two
    // same-named files under different subdirectories (e.g. a/src/util.cpp
    // and b/src/util.cpp) never fold onto the same object output. Left
    // default-constructed (empty) for units synthesized outside the
    // scanner (e.g. plan.cppm's ad-hoc main.cpp CompileUnit, which never
    // reads this field).
    std::filesystem::path           relPath;
    std::string                     packageName;
    std::vector localIncludeDirs;
    // #249: dirs emitted as -idirafter  searched after the toolchain's
    // system dirs, so a dep's source root can't shadow standard headers.
    std::vector localIncludeDirsAfter;
    std::vector        packageCflags;
    std::vector        packageCxxflags;
    std::vector        packageAsmflags;   // per-glob asmflags (G4)
    std::optional         provides;
    // Was `provides` declared with `export module`?
    //
    // Both spellings produce a BMI and an object, so `provides` alone cannot
    // tell an INTERFACE partition (`export module M:api;`) from an
    // IMPLEMENTATION partition (`module M:impl;`)  and the difference decides
    // whether a source may be published. `mcpp pack` publishes the module
    // closure of the lib root; a closure that reaches an implementation
    // partition has to publish it too (the consumer cannot build the BMI
    // without it), and the author needs to be told that their implementation
    // is going out.
    //
    // Three states, because "nobody determined this" is a real answer and it
    // used to be spelled `true`:
    //
    //   true     `export module M:api;`  the text scanner read the keyword
    //   false    `module M:impl;`        likewise
    //   nullopt  nobody could tell: a `scan_overrides` entry names the module
    //            but has no way to say whether it is exported, and a P1689
    //            scanner may omit `is-interface`
    //
    // It defaulted to `true` and was called the conservative direction "since
    // the flag only ever produces a warning". That had it backwards: `true` is
    // the value that produces NO warning, so an undetermined implementation
    // partition was published in silence  the exact failure this field exists
    // to prevent. Unknown now warns, naming the file and the reason.
    std::optional             providesInterface;
    std::vector           requires_;
    // The unit's ROLE, decided once by the scanner from the owning package's
    // extension table and carried from here on. Every downstream consumer
    // (planner, backend, compile_commands, the asm dialect check) reads this
    // instead of re-deriving it from the extension  see mcpp.source_kind for
    // why that mattered.
    mcpp::SourceKind                kind = mcpp::SourceKind::Other;
    //
    // `isModuleInterface` / `isImplementation` used to live here. They were
    // WRITE-ONLY: three sites derived them (the scanner said `.cpp`, the p1689
    // reader said `.cpp || .cxx`, the scan_overrides branch said "has
    // provides"), the three disagreed, and nothing in src/ or tests/ ever read
    // the result. Converging three derivations of a value nobody reads is
    // still three derivations, so they are gone instead. `provides` answers
    // "is this an interface"; `kind` answers the rest.
    // Unit built from a manifest scan_overrides declaration instead of a
    // real scan  plan-vs-ddi verification is mandatory for these.
    bool                            scanOverridden     = false;
};

struct Graph {
    std::vector                                  units;
    // logical-name -> index into units
    std::map             producerOf;
    // edges as (consumer-index, producer-index)
    std::vector         edges;
};

// Topological order: returns indices of units in producer-before-consumer order.
// Returns std::unexpected with the cycle if any.
struct CycleError {
    std::vector cycle;
};
std::expected topo_sort(const Graph& g);

} // namespace mcpp::modgraph

namespace mcpp::modgraph {

std::expected topo_sort(const Graph& g) {
    std::vector indeg(g.units.size(), 0);
    std::vector adj(g.units.size());
    for (auto [c, p] : g.edges) {
        // edge means: consumer depends on producer. So producer must come first.
        // indegree of consumer counts unmet producer dependencies.
        indeg[c]++;
        adj[p].push_back(c);
    }

    std::vector order;
    order.reserve(g.units.size());
    std::vector queue;
    for (std::size_t i = 0; i < indeg.size(); ++i) {
        if (indeg[i] == 0) queue.push_back(i);
    }
    while (!queue.empty()) {
        std::size_t u = queue.back();
        queue.pop_back();
        order.push_back(u);
        for (auto v : adj[u]) {
            if (--indeg[v] == 0) queue.push_back(v);
        }
    }
    if (order.size() != g.units.size()) {
        // Cycle remains. Report units still with positive indegree.
        CycleError err;
        for (std::size_t i = 0; i < indeg.size(); ++i) {
            if (indeg[i] > 0) err.cycle.push_back(i);
        }
        return std::unexpected(err);
    }
    return order;
}

} // namespace mcpp::modgraph

Web Proxy Viewer  |  New URL  |  Original Page