diff --git a/include/pup/core/string_id.hpp b/include/pup/core/string_id.hpp index 383b1557..8a797cdf 100644 --- a/include/pup/core/string_id.hpp +++ b/include/pup/core/string_id.hpp @@ -3,6 +3,7 @@ #pragma once +#include #include namespace pup { @@ -36,10 +37,13 @@ constexpr auto make_string_id(std::uint32_t value) -> StringId /// means: `handle_less` for a set, or a pool projection for anything observable. /// The built-in enum comparison is interning order, and the two spellings were /// indistinguishable — issues #171, #175, #183 and #184 were all that confusion. +/// <=> is deleted too, or a defaulted operator<=> on any struct holding a StringId +/// would order by handle without naming it. auto operator<(StringId, StringId) -> bool = delete; auto operator>(StringId, StringId) -> bool = delete; auto operator<=(StringId, StringId) -> bool = delete; auto operator>=(StringId, StringId) -> bool = delete; +auto operator<=>(StringId, StringId) -> std::strong_ordering = delete; /// Order by interning handle. Valid only where the order is unobservable — /// membership, dedup, binary search against a handle-keyed range. diff --git a/src/cli/context.cpp b/src/cli/context.cpp index ab1b2b8a..90a93741 100644 --- a/src/cli/context.cpp +++ b/src/cli/context.cpp @@ -460,7 +460,10 @@ auto compose_nested_project_subtree(std::string_view dir, ParseContext& ctx) -> for (auto sub_id : sub_dirs) { auto sub_sv = pool.get(sub_id); auto rel_id = (sub_sv == ".") ? pool.intern(marker_prefix) : pup::path::join(marker_prefix, sub_sv); - if (!std::binary_search(available.begin(), available.end(), rel_id, pup::handle_less)) { + // Search only the sorted prefix: appending unsorts the tail, and a binary search + // over that reports a present directory absent, so it gets parsed twice. + auto* sorted_end = available.begin() + static_cast(before); + if (!std::binary_search(available.begin(), sorted_end, rel_id, pup::handle_less)) { available.push_back(rel_id); } } @@ -468,6 +471,7 @@ auto compose_nested_project_subtree(std::string_view dir, ParseContext& ctx) -> return false; } std::sort(available.begin(), available.end(), pup::handle_less); + available.erase(std::unique(available.begin(), available.end()), available.end()); return true; } diff --git a/src/exec/scheduler.cpp b/src/exec/scheduler.cpp index 1147eaf0..38654dac 100644 --- a/src/exec/scheduler.cpp +++ b/src/exec/scheduler.cpp @@ -126,7 +126,15 @@ auto prepare_job_launch( for (auto var_id : base_env) { env_ids.push_back(var_id); } + // Handle order reaches the child: create_env_block (process-win32.cpp:102) emits this + // sequence verbatim, and Win32 expects an alphabetically sorted block. + auto exported = Vec {}; for (auto var_id : job.exported_vars) { + exported.push_back(static_cast(var_id)); + } + std::ranges::sort(exported, {}, [&pool](StringId id) { return pool.get(id); }); + + for (auto var_id : exported) { auto var_sv = pool.get(var_id); if (auto it = env_cache_find(env_cache, var_sv)) { auto eb = Buf {};