From d9771ca48a9213b29f748f68c39880257a41c08b Mon Sep 17 00:00:00 2001 From: Gennaro Prota Date: Thu, 2 Jul 2026 11:17:16 +0200 Subject: [PATCH 1/3] chore: bump LLVM to 77e43ec1 (May 21, 2026) The new pin includes llvm/llvm-project#198452, which attaches documentation comments to macro definitions. That is the prerequisite for reading them from Clang directly instead of scanning the source, as we were initially doing in PR #1192. The pin was about six months behind, so this also adapts MrDocs to the Clang API changes across that range: - USRGeneration.h moved from clang/Index to clang/UnifiedSymbolResolution; the `clang::index` namespace and signatures are unchanged. - `clang::DiagnosticConsumer::finish()` was removed, so the collecting diagnostic consumer relies on the forwarded `EndSourceFile()` instead. - The driver option table moved to clang/Options/Options.h, with its enumerators now in `clang::options` and `getDriverOptTable()` in `clang`. - `cl::getRegisteredOptions()` now returns a `DenseMap`, so its entries expose `.second` instead of `getValue()`. - `SubstituteConstraintExpressionWithoutSatisfaction` was re-synced with Clang's current implementation. Parsing the newer libc++ also needs a new vcruntime_new.h stub (its align_val_t.h now includes it under the vcruntime ABI) and C++ guards so stdbool.h, stdalign.h, and threads.h stop redefining some C++ keywords as macros. The regenerated goldens change only because Clang's output drifted over the six months: constrained templates and destructors get different symbol IDs from USR generation, though their structure is identical (requires-clause, dtor-overloads), and a few declaration locations changed. --- .github/workflows/ci-matrix.yml | 2 +- share/mrdocs/headers/libc-stubs/new.h | 24 +- share/mrdocs/headers/libc-stubs/stdalign.h | 3 +- share/mrdocs/headers/libc-stubs/stdbool.h | 3 +- share/mrdocs/headers/libc-stubs/threads.h | 3 +- .../mrdocs/headers/libc-stubs/vcruntime_new.h | 38 +++ src/lib/AST/ASTVisitor.cpp | 2 +- src/lib/AST/ClangHelpers.cpp | 87 ++++-- src/lib/AST/MissingSymbolSink.hpp | 10 - src/lib/MrDocsCompilationDatabase.cpp | 274 +++++++++--------- src/test/TestArgs.cpp | 6 +- src/tool/ToolArgs.cpp | 4 +- .../javadoc/copydoc/param-types.xml | 4 +- .../symbols/concept/requires-clause.xml | 20 +- .../golden-tests/symbols/function/auto.xml | 2 +- .../symbols/function/function-template.xml | 4 +- .../symbols/record/dtor-overloads.xml | 8 +- third-party/recipes/llvm.json | 6 +- util/bootstrap/tests/test_cache_keys.py | 38 +-- 19 files changed, 290 insertions(+), 248 deletions(-) create mode 100644 share/mrdocs/headers/libc-stubs/vcruntime_new.h diff --git a/.github/workflows/ci-matrix.yml b/.github/workflows/ci-matrix.yml index d32e2ca145..cf5a4c2f74 100644 --- a/.github/workflows/ci-matrix.yml +++ b/.github/workflows/ci-matrix.yml @@ -82,7 +82,7 @@ jobs: llvm-runtimes: {{{select (ine use-libcxx 'true') libcxx-runtimes ""}}} # LLVM build configuration - llvm-hash: dc4cef81d47c7bc4a3c4d58fbacf8a6359683fae + llvm-hash: 77e43ec11cd8fbe1de491118b54de9bba94510a8 llvm-short-hash: {{{substr llvm-hash 0 7}}} llvm-preset-build-type: {{{lowercase build-type}}} llvm-preset-os: {{select (ieq os 'windows') "win" "unix"}} diff --git a/share/mrdocs/headers/libc-stubs/new.h b/share/mrdocs/headers/libc-stubs/new.h index 8366be759f..55d503cc9c 100644 --- a/share/mrdocs/headers/libc-stubs/new.h +++ b/share/mrdocs/headers/libc-stubs/new.h @@ -12,29 +12,7 @@ #define MRDOCS_SHARE_HEADERS_LIBC_STUBS_NEW_H #if defined(_LIBCPP_ABI_VCRUNTIME) -namespace std { - enum class align_val_t : size_t {}; - struct nothrow_t { explicit nothrow_t() = default; }; - extern const std::nothrow_t nothrow; -} - -// Replaceable allocation functions -void* operator new ( std::size_t count ); -void* operator new[]( std::size_t count ); -void* operator new ( std::size_t count, std::align_val_t al ); -void* operator new[]( std::size_t count, std::align_val_t al ); - -// Replaceable non-throwing allocation functions -void* operator new ( std::size_t count, const std::nothrow_t& tag ); -void* operator new[]( std::size_t count, const std::nothrow_t& tag ); -void* operator new ( std::size_t count, std::align_val_t al, - const std::nothrow_t& tag ) noexcept; -void* operator new[]( std::size_t count, std::align_val_t al, - const std::nothrow_t& tag ) noexcept; - -// Non-allocating placement allocation functions -void* operator new ( std::size_t count, void* ptr ); -void* operator new[]( std::size_t count, void* ptr ); +#include #endif #endif // MRDOCS_SHARE_HEADERS_LIBC_STUBS_NEW_H diff --git a/share/mrdocs/headers/libc-stubs/stdalign.h b/share/mrdocs/headers/libc-stubs/stdalign.h index 886fbf6d38..13a31852a0 100644 --- a/share/mrdocs/headers/libc-stubs/stdalign.h +++ b/share/mrdocs/headers/libc-stubs/stdalign.h @@ -11,9 +11,10 @@ #ifndef MRDOCS_SHARE_HEADERS_LIBC_STUBS_STDALIGN_H #define MRDOCS_SHARE_HEADERS_LIBC_STUBS_STDALIGN_H -// Convenience macros for alignment +#if !defined __cplusplus #define alignas _Alignas #define alignof _Alignof +#endif // Macro constants indicating the presence of alignment features #define __alignas_is_defined 1 diff --git a/share/mrdocs/headers/libc-stubs/stdbool.h b/share/mrdocs/headers/libc-stubs/stdbool.h index 7dffbc9518..ad75da519b 100644 --- a/share/mrdocs/headers/libc-stubs/stdbool.h +++ b/share/mrdocs/headers/libc-stubs/stdbool.h @@ -11,10 +11,11 @@ #ifndef MRDOCS_SHARE_HEADERS_LIBC_STUBS_STDBOOL_H #define MRDOCS_SHARE_HEADERS_LIBC_STUBS_STDBOOL_H -// Convenience macros for boolean values +#if !defined __cplusplus #define bool _Bool #define true 1 #define false 0 +#endif // Macro constant indicating the presence of boolean features #define __bool_true_false_are_defined 1 diff --git a/share/mrdocs/headers/libc-stubs/threads.h b/share/mrdocs/headers/libc-stubs/threads.h index 2e2ce49299..8dad25cc92 100644 --- a/share/mrdocs/headers/libc-stubs/threads.h +++ b/share/mrdocs/headers/libc-stubs/threads.h @@ -73,8 +73,9 @@ int cnd_wait(cnd_t *cond, mtx_t *mtx); int cnd_timedwait(cnd_t *cond, mtx_t *mtx, const struct timespec *ts); void cnd_destroy(cnd_t *cond); -// Thread-local storage +#if !defined __cplusplus #define thread_local _Thread_local +#endif typedef void (*tss_dtor_t)(void *); typedef struct { diff --git a/share/mrdocs/headers/libc-stubs/vcruntime_new.h b/share/mrdocs/headers/libc-stubs/vcruntime_new.h new file mode 100644 index 0000000000..4c5d9a06f6 --- /dev/null +++ b/share/mrdocs/headers/libc-stubs/vcruntime_new.h @@ -0,0 +1,38 @@ +// +// Licensed under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// Copyright (c) 2024 Alan de Freitas (alandefreitas@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#ifndef MRDOCS_SHARE_HEADERS_LIBC_STUBS_VCRUNTIME_NEW_H +#define MRDOCS_SHARE_HEADERS_LIBC_STUBS_VCRUNTIME_NEW_H + +namespace std { + enum class align_val_t : size_t {}; + struct nothrow_t { explicit nothrow_t() = default; }; + extern const std::nothrow_t nothrow; +} + +// Replaceable allocation functions +void* operator new ( std::size_t count ); +void* operator new[]( std::size_t count ); +void* operator new ( std::size_t count, std::align_val_t al ); +void* operator new[]( std::size_t count, std::align_val_t al ); + +// Replaceable non-throwing allocation functions +void* operator new ( std::size_t count, const std::nothrow_t& tag ); +void* operator new[]( std::size_t count, const std::nothrow_t& tag ); +void* operator new ( std::size_t count, std::align_val_t al, + const std::nothrow_t& tag ) noexcept; +void* operator new[]( std::size_t count, std::align_val_t al, + const std::nothrow_t& tag ) noexcept; + +// Non-allocating placement allocation functions +void* operator new ( std::size_t count, void* ptr ); +void* operator new[]( std::size_t count, void* ptr ); + +#endif // MRDOCS_SHARE_HEADERS_LIBC_STUBS_VCRUNTIME_NEW_H diff --git a/src/lib/AST/ASTVisitor.cpp b/src/lib/AST/ASTVisitor.cpp index 89390556bc..c222e4aaac 100644 --- a/src/lib/AST/ASTVisitor.cpp +++ b/src/lib/AST/ASTVisitor.cpp @@ -32,11 +32,11 @@ #include #include #include -#include #include #include #include #include +#include #include #include #include diff --git a/src/lib/AST/ClangHelpers.cpp b/src/lib/AST/ClangHelpers.cpp index 0b72ce1dd4..b53f245883 100644 --- a/src/lib/AST/ClangHelpers.cpp +++ b/src/lib/AST/ClangHelpers.cpp @@ -6,6 +6,7 @@ // // Copyright (c) 2023 Vinnie Falco (vinnie.falco@gmail.com) // Copyright (c) 2023 Krystian Stasiowski (sdkrystian@gmail.com) +// Copyright (c) 2026 Gennaro Prota (gennaro.prota@gmail.com) // // Official repository: https://github.com/cppalliance/mrdocs // @@ -14,8 +15,9 @@ #include #include #include -#include +#include #include +#include #include #include @@ -27,7 +29,7 @@ SubstituteConstraintExpressionWithoutSatisfaction( clang::Expr const* ConstrExpr) { clang::MultiLevelTemplateArgumentList MLTAL = S.getTemplateInstantiationArgs( - DeclInfo.getDecl(), DeclInfo.getLexicalDeclContext(), /*Final=*/false, + DeclInfo.getDecl(), DeclInfo.getDeclContext(), /*Final=*/false, /*Innermost=*/std::nullopt, /*RelativeToPrimary=*/true, /*Pattern=*/nullptr, /*ForConstraintInstantiation=*/true, @@ -38,31 +40,48 @@ SubstituteConstraintExpressionWithoutSatisfaction( return ConstrExpr; } + // Clang's own `AreConstraintExpressionsEqual` wraps the calls to this + // helper in an `SFINAETrap`. MrDocs invokes the helper directly, so the + // trap lives here instead: it traps substitution errors and supplies the + // SFINAE context that `SubstConstraintExprWithoutSatisfaction` expects on + // the instantiation stack. clang::Sema::SFINAETrap const SFINAE(S, /*AccessCheckingSFINAE=*/false); - clang::Sema::InstantiatingTemplate Inst( - S, DeclInfo.getLocation(), - clang::Sema::InstantiatingTemplate::ConstraintNormalization{}, - const_cast(DeclInfo.getDecl()), clang::SourceRange{}); - if (Inst.isInvalid()) - { - return nullptr; - } - // Set up a dummy 'instantiation' scope in the case of reference to function // parameters that the surrounding function hasn't been instantiated yet. Note // this may happen while we're comparing two templates' constraint // equivalence. - clang::LocalInstantiationScope ScopeForParameters(S); - if (auto *FD = DeclInfo.getDecl()->getAsFunction()) - { - for (auto *PVD : FD->parameters()) + std::optional ScopeForParameters; + if (clang::NamedDecl const* ND = DeclInfo.getDecl(); + ND && ND->isFunctionOrFunctionTemplate()) + { + ScopeForParameters.emplace(S, /*CombineWithOuterScope=*/true); + clang::FunctionDecl const* FD = ND->getAsFunction(); + if (clang::FunctionTemplateDecl* Template = + FD->getDescribedFunctionTemplate(); + Template && Template->getInstantiatedFromMemberTemplate()) { - ScopeForParameters.InstantiatedLocal(PVD, PVD); + FD = Template->getInstantiatedFromMemberTemplate()->getTemplatedDecl(); + } + for (auto* PVD : FD->parameters()) + { + if (ScopeForParameters->getInstantiationOfIfExists(PVD)) + { + continue; + } + if (!PVD->isParameterPack()) + { + ScopeForParameters->InstantiatedLocal(PVD, PVD); + continue; + } + // Map the parameter pack to a size-of-1 argument so its canonical + // type is used when comparing redeclarations for equivalence. + ScopeForParameters->MakeInstantiatedLocalArgPack(PVD); + ScopeForParameters->InstantiatedLocalPackArg(PVD, PVD); } } - Optional ThisScope; + std::optional ThisScope; // See TreeTransform::RebuildTemplateSpecializationType. A context scope is // essential for having an injected class as the canonical type for a template @@ -71,13 +90,27 @@ SubstituteConstraintExpressionWithoutSatisfaction( // template specializations can be profiled to the same value, which makes it // possible that e.g. constraints involving C> and C are // perceived identical. - Optional ContextScope; - if (auto *RD = dyn_cast(DeclInfo.getDeclContext())) - { - ThisScope.emplace(S, const_cast(RD), clang::Qualifiers()); - ContextScope.emplace(S, const_cast(cast(RD)), - /*NewThisContext=*/false); - } + std::optional ContextScope; + clang::DeclContext const* DC = [&] { + if (!DeclInfo.getDecl()) + { + return DeclInfo.getDeclContext(); + } + return DeclInfo.getDecl()->getFriendObjectKind() + ? DeclInfo.getLexicalDeclContext() + : DeclInfo.getDeclContext(); + }(); + if (auto* RD = dyn_cast(DC)) + { + ThisScope.emplace( + S, const_cast(RD), clang::Qualifiers()); + ContextScope.emplace( + S, const_cast(cast(RD)), + /*NewThisContext=*/false); + } + clang::EnterExpressionEvaluationContext UnevaluatedContext( + S, clang::Sema::ExpressionEvaluationContext::Unevaluated, + clang::Sema::ReuseLambdaContextDecl); clang::ExprResult SubstConstr = S.SubstConstraintExprWithoutSatisfaction( const_cast(ConstrExpr), MLTAL); if (SFINAE.hasErrorOccurred() || !SubstConstr.isUsable()) @@ -475,15 +508,15 @@ isStaticFileLevelMember(clang::Decl const* D) clang::RawComment const* getDocumentation(clang::Decl const* D) { - clang::RawComment const* RC = - D->getASTContext().getRawCommentForDeclNoCache(D); + clang::ASTContext const& ctx = D->getASTContext(); + clang::RawComment const* RC = ctx.getRawCommentNoCache(D); if (!RC) { auto const* TD = dyn_cast(D); MRDOCS_CHECK_OR(TD, nullptr); clang::NamedDecl const* ND = TD->getTemplatedDecl(); MRDOCS_CHECK_OR(ND, nullptr); - RC = ND->getASTContext().getRawCommentForDeclNoCache(ND); + RC = ctx.getRawCommentNoCache(ND); } return RC; } diff --git a/src/lib/AST/MissingSymbolSink.hpp b/src/lib/AST/MissingSymbolSink.hpp index 87bbf04e5c..391d307bf5 100644 --- a/src/lib/AST/MissingSymbolSink.hpp +++ b/src/lib/AST/MissingSymbolSink.hpp @@ -423,16 +423,6 @@ class CollectingDiagConsumer : public clang::DiagnosticConsumer { Downstream_->EndSourceFile(); } - - - void - finish() override - { - if (Downstream_) - { - Downstream_->finish(); - } - } }; } // namespace mrdocs diff --git a/src/lib/MrDocsCompilationDatabase.cpp b/src/lib/MrDocsCompilationDatabase.cpp index 730c936b5e..c7aed4e237 100644 --- a/src/lib/MrDocsCompilationDatabase.cpp +++ b/src/lib/MrDocsCompilationDatabase.cpp @@ -18,8 +18,8 @@ #include #include #include -#include #include +#include #include #include #include @@ -96,153 +96,153 @@ isValidMrDocsOption( if (optionMatchesAny(opt, // unknown options - clang::driver::options::OPT_UNKNOWN, + clang::options::OPT_UNKNOWN, // sanitizers - clang::driver::options::OPT_fsanitize_EQ, - clang::driver::options::OPT_fno_sanitize_EQ, - clang::driver::options::OPT_fsanitize_recover_EQ, - clang::driver::options::OPT_fno_sanitize_recover_EQ, - clang::driver::options::OPT_fsanitize_trap_EQ, - clang::driver::options::OPT_fno_sanitize_trap_EQ, - clang::driver::options::OPT_fsanitize_address_use_after_scope, - clang::driver::options::OPT_fexperimental_sanitize_metadata_ignorelist_EQ, - clang::driver::options::OPT_fexperimental_sanitize_metadata_EQ_atomics, - clang::driver::options::OPT_fexperimental_sanitize_metadata_EQ_covered, - clang::driver::options::OPT_fexperimental_sanitize_metadata_EQ, - clang::driver::options::OPT_fgpu_sanitize, - clang::driver::options::OPT_fno_experimental_sanitize_metadata_EQ, - clang::driver::options::OPT_fno_gpu_sanitize, - clang::driver::options::OPT_fno_sanitize_address_globals_dead_stripping, - clang::driver::options::OPT_fno_sanitize_address_outline_instrumentation, - clang::driver::options::OPT_fno_sanitize_address_poison_custom_array_cookie, - clang::driver::options::OPT_fno_sanitize_address_use_after_scope, - clang::driver::options::OPT_fno_sanitize_address_use_odr_indicator, - clang::driver::options::OPT__SLASH_fno_sanitize_address_vcasan_lib, - clang::driver::options::OPT_fno_sanitize_cfi_canonical_jump_tables, - clang::driver::options::OPT_fno_sanitize_cfi_cross_dso, - clang::driver::options::OPT_fno_sanitize_coverage, - clang::driver::options::OPT_fno_sanitize_hwaddress_experimental_aliasing, - clang::driver::options::OPT_fno_sanitize_ignorelist, - clang::driver::options::OPT_fno_sanitize_link_cxx_runtime, - clang::driver::options::OPT_fno_sanitize_link_runtime, - clang::driver::options::OPT_fno_sanitize_memory_param_retval, - clang::driver::options::OPT_fno_sanitize_memory_track_origins, - clang::driver::options::OPT_fno_sanitize_memory_use_after_dtor, - clang::driver::options::OPT_fno_sanitize_minimal_runtime, - clang::driver::options::OPT_fno_sanitize_recover_EQ, - clang::driver::options::OPT_fno_sanitize_recover, - clang::driver::options::OPT_fno_sanitize_stable_abi, - clang::driver::options::OPT_fno_sanitize_stats, - clang::driver::options::OPT_fno_sanitize_thread_atomics, - clang::driver::options::OPT_fno_sanitize_thread_func_entry_exit, - clang::driver::options::OPT_fno_sanitize_thread_memory_access, - clang::driver::options::OPT_fno_sanitize_trap_EQ, - clang::driver::options::OPT_fno_sanitize_trap, - clang::driver::options::OPT_fno_sanitize_undefined_trap_on_error, - clang::driver::options::OPT_fno_sanitize_EQ, - clang::driver::options::OPT_sanitize_address_destructor_EQ, - clang::driver::options::OPT_fsanitize_address_field_padding, - clang::driver::options::OPT_fsanitize_address_globals_dead_stripping, - clang::driver::options::OPT_fsanitize_address_outline_instrumentation, - clang::driver::options::OPT_fsanitize_address_poison_custom_array_cookie, - clang::driver::options::OPT_sanitize_address_use_after_return_EQ, - clang::driver::options::OPT__SLASH_fsanitize_address_use_after_return, - clang::driver::options::OPT_fsanitize_address_use_after_scope, - clang::driver::options::OPT_fsanitize_address_use_odr_indicator, - clang::driver::options::OPT_fsanitize_cfi_canonical_jump_tables, - clang::driver::options::OPT_fsanitize_cfi_cross_dso, - clang::driver::options::OPT_fsanitize_cfi_icall_normalize_integers, - clang::driver::options::OPT_fsanitize_cfi_icall_generalize_pointers, - clang::driver::options::OPT_fsanitize_coverage_8bit_counters, - clang::driver::options::OPT_fsanitize_coverage_allowlist, - clang::driver::options::OPT_fsanitize_coverage_control_flow, - clang::driver::options::OPT_fsanitize_coverage_ignorelist, - clang::driver::options::OPT_fsanitize_coverage_indirect_calls, - clang::driver::options::OPT_fsanitize_coverage_inline_8bit_counters, - clang::driver::options::OPT_fsanitize_coverage_inline_bool_flag, - clang::driver::options::OPT_fsanitize_coverage_no_prune, - clang::driver::options::OPT_fsanitize_coverage_pc_table, - clang::driver::options::OPT_fsanitize_coverage_stack_depth, - clang::driver::options::OPT_fsanitize_coverage_trace_bb, - clang::driver::options::OPT_fsanitize_coverage_trace_cmp, - clang::driver::options::OPT_fsanitize_coverage_trace_div, - clang::driver::options::OPT_fsanitize_coverage_trace_gep, - clang::driver::options::OPT_fsanitize_coverage_trace_loads, - clang::driver::options::OPT_fsanitize_coverage_trace_pc_guard, - clang::driver::options::OPT_fsanitize_coverage_trace_pc, - clang::driver::options::OPT_fsanitize_coverage_trace_stores, - clang::driver::options::OPT_fsanitize_coverage_type, - clang::driver::options::OPT_fsanitize_coverage, - clang::driver::options::OPT_fsanitize_hwaddress_abi_EQ, - clang::driver::options::OPT_fsanitize_hwaddress_experimental_aliasing, - clang::driver::options::OPT_fsanitize_ignorelist_EQ, - clang::driver::options::OPT_fsanitize_link_cxx_runtime, - clang::driver::options::OPT_fsanitize_link_runtime, - clang::driver::options::OPT_fsanitize_memory_param_retval, - clang::driver::options::OPT_fsanitize_memory_track_origins_EQ, - clang::driver::options::OPT_fsanitize_memory_track_origins, - clang::driver::options::OPT_fsanitize_memory_use_after_dtor, - clang::driver::options::OPT_fsanitize_memtag_mode_EQ, - clang::driver::options::OPT_fsanitize_minimal_runtime, - clang::driver::options::OPT_fsanitize_recover_EQ, - clang::driver::options::OPT_fsanitize_recover, - clang::driver::options::OPT_fsanitize_stable_abi, - clang::driver::options::OPT_fsanitize_stats, - clang::driver::options::OPT_fsanitize_system_ignorelist_EQ, - clang::driver::options::OPT_fsanitize_thread_atomics, - clang::driver::options::OPT_fsanitize_thread_func_entry_exit, - clang::driver::options::OPT_fsanitize_thread_memory_access, - clang::driver::options::OPT_fsanitize_trap_EQ, - clang::driver::options::OPT_fsanitize_trap, - clang::driver::options::OPT_fsanitize_undefined_strip_path_components_EQ, - clang::driver::options::OPT_fsanitize_undefined_trap_on_error, - clang::driver::options::OPT__SLASH_fsanitize_EQ_address, - clang::driver::options::OPT_fsanitize_EQ, - clang::driver::options::OPT_shared_libsan, - clang::driver::options::OPT_static_libsan, - clang::driver::options::OPT_static_libsan, + clang::options::OPT_fsanitize_EQ, + clang::options::OPT_fno_sanitize_EQ, + clang::options::OPT_fsanitize_recover_EQ, + clang::options::OPT_fno_sanitize_recover_EQ, + clang::options::OPT_fsanitize_trap_EQ, + clang::options::OPT_fno_sanitize_trap_EQ, + clang::options::OPT_fsanitize_address_use_after_scope, + clang::options::OPT_fexperimental_sanitize_metadata_ignorelist_EQ, + clang::options::OPT_fexperimental_sanitize_metadata_EQ_atomics, + clang::options::OPT_fexperimental_sanitize_metadata_EQ_covered, + clang::options::OPT_fexperimental_sanitize_metadata_EQ, + clang::options::OPT_fgpu_sanitize, + clang::options::OPT_fno_experimental_sanitize_metadata_EQ, + clang::options::OPT_fno_gpu_sanitize, + clang::options::OPT_fno_sanitize_address_globals_dead_stripping, + clang::options::OPT_fno_sanitize_address_outline_instrumentation, + clang::options::OPT_fno_sanitize_address_poison_custom_array_cookie, + clang::options::OPT_fno_sanitize_address_use_after_scope, + clang::options::OPT_fno_sanitize_address_use_odr_indicator, + clang::options::OPT__SLASH_fno_sanitize_address_vcasan_lib, + clang::options::OPT_fno_sanitize_cfi_canonical_jump_tables, + clang::options::OPT_fno_sanitize_cfi_cross_dso, + clang::options::OPT_fno_sanitize_coverage, + clang::options::OPT_fno_sanitize_hwaddress_experimental_aliasing, + clang::options::OPT_fno_sanitize_ignorelist, + clang::options::OPT_fno_sanitize_link_cxx_runtime, + clang::options::OPT_fno_sanitize_link_runtime, + clang::options::OPT_fno_sanitize_memory_param_retval, + clang::options::OPT_fno_sanitize_memory_track_origins, + clang::options::OPT_fno_sanitize_memory_use_after_dtor, + clang::options::OPT_fno_sanitize_minimal_runtime, + clang::options::OPT_fno_sanitize_recover_EQ, + clang::options::OPT_fno_sanitize_recover, + clang::options::OPT_fno_sanitize_stable_abi, + clang::options::OPT_fno_sanitize_stats, + clang::options::OPT_fno_sanitize_thread_atomics, + clang::options::OPT_fno_sanitize_thread_func_entry_exit, + clang::options::OPT_fno_sanitize_thread_memory_access, + clang::options::OPT_fno_sanitize_trap_EQ, + clang::options::OPT_fno_sanitize_trap, + clang::options::OPT_fno_sanitize_undefined_trap_on_error, + clang::options::OPT_fno_sanitize_EQ, + clang::options::OPT_sanitize_address_destructor_EQ, + clang::options::OPT_fsanitize_address_field_padding, + clang::options::OPT_fsanitize_address_globals_dead_stripping, + clang::options::OPT_fsanitize_address_outline_instrumentation, + clang::options::OPT_fsanitize_address_poison_custom_array_cookie, + clang::options::OPT_sanitize_address_use_after_return_EQ, + clang::options::OPT__SLASH_fsanitize_address_use_after_return, + clang::options::OPT_fsanitize_address_use_after_scope, + clang::options::OPT_fsanitize_address_use_odr_indicator, + clang::options::OPT_fsanitize_cfi_canonical_jump_tables, + clang::options::OPT_fsanitize_cfi_cross_dso, + clang::options::OPT_fsanitize_cfi_icall_normalize_integers, + clang::options::OPT_fsanitize_cfi_icall_generalize_pointers, + clang::options::OPT_fsanitize_coverage_8bit_counters, + clang::options::OPT_fsanitize_coverage_allowlist, + clang::options::OPT_fsanitize_coverage_control_flow, + clang::options::OPT_fsanitize_coverage_ignorelist, + clang::options::OPT_fsanitize_coverage_indirect_calls, + clang::options::OPT_fsanitize_coverage_inline_8bit_counters, + clang::options::OPT_fsanitize_coverage_inline_bool_flag, + clang::options::OPT_fsanitize_coverage_no_prune, + clang::options::OPT_fsanitize_coverage_pc_table, + clang::options::OPT_fsanitize_coverage_stack_depth, + clang::options::OPT_fsanitize_coverage_trace_bb, + clang::options::OPT_fsanitize_coverage_trace_cmp, + clang::options::OPT_fsanitize_coverage_trace_div, + clang::options::OPT_fsanitize_coverage_trace_gep, + clang::options::OPT_fsanitize_coverage_trace_loads, + clang::options::OPT_fsanitize_coverage_trace_pc_guard, + clang::options::OPT_fsanitize_coverage_trace_pc, + clang::options::OPT_fsanitize_coverage_trace_stores, + clang::options::OPT_fsanitize_coverage_type, + clang::options::OPT_fsanitize_coverage, + clang::options::OPT_fsanitize_hwaddress_abi_EQ, + clang::options::OPT_fsanitize_hwaddress_experimental_aliasing, + clang::options::OPT_fsanitize_ignorelist_EQ, + clang::options::OPT_fsanitize_link_cxx_runtime, + clang::options::OPT_fsanitize_link_runtime, + clang::options::OPT_fsanitize_memory_param_retval, + clang::options::OPT_fsanitize_memory_track_origins_EQ, + clang::options::OPT_fsanitize_memory_track_origins, + clang::options::OPT_fsanitize_memory_use_after_dtor, + clang::options::OPT_fsanitize_memtag_mode_EQ, + clang::options::OPT_fsanitize_minimal_runtime, + clang::options::OPT_fsanitize_recover_EQ, + clang::options::OPT_fsanitize_recover, + clang::options::OPT_fsanitize_stable_abi, + clang::options::OPT_fsanitize_stats, + clang::options::OPT_fsanitize_system_ignorelist_EQ, + clang::options::OPT_fsanitize_thread_atomics, + clang::options::OPT_fsanitize_thread_func_entry_exit, + clang::options::OPT_fsanitize_thread_memory_access, + clang::options::OPT_fsanitize_trap_EQ, + clang::options::OPT_fsanitize_trap, + clang::options::OPT_fsanitize_undefined_strip_path_components_EQ, + clang::options::OPT_fsanitize_undefined_trap_on_error, + clang::options::OPT__SLASH_fsanitize_EQ_address, + clang::options::OPT_fsanitize_EQ, + clang::options::OPT_shared_libsan, + clang::options::OPT_static_libsan, + clang::options::OPT_static_libsan, // diagnostic options - clang::driver::options::OPT_Diag_Group, - clang::driver::options::OPT_W_value_Group, - clang::driver::options::OPT__SLASH_wd, + clang::options::OPT_Diag_Group, + clang::options::OPT_W_value_Group, + clang::options::OPT__SLASH_wd, // language conformance options - clang::driver::options::OPT_pedantic_Group, - clang::driver::options::OPT__SLASH_permissive, - clang::driver::options::OPT__SLASH_permissive_, + clang::options::OPT_pedantic_Group, + clang::options::OPT__SLASH_permissive, + clang::options::OPT__SLASH_permissive_, // ignored options - clang::driver::options::OPT_cl_ignored_Group, - clang::driver::options::OPT_cl_ignored_Group, - clang::driver::options::OPT_clang_ignored_f_Group, - clang::driver::options::OPT_clang_ignored_gcc_optimization_f_Group, - clang::driver::options::OPT_clang_ignored_legacy_options_Group, - clang::driver::options::OPT_clang_ignored_m_Group, - clang::driver::options::OPT_flang_ignored_w_Group + clang::options::OPT_cl_ignored_Group, + clang::options::OPT_cl_ignored_Group, + clang::options::OPT_clang_ignored_f_Group, + clang::options::OPT_clang_ignored_gcc_optimization_f_Group, + clang::options::OPT_clang_ignored_legacy_options_Group, + clang::options::OPT_clang_ignored_m_Group, + clang::options::OPT_flang_ignored_w_Group #if 0 // input file options - clang::driver::options::OPT_INPUT, + clang::options::OPT_INPUT, // output file options - clang::driver::options::OPT_o, - clang::driver::options::OPT__SLASH_o, - clang::driver::options::OPT__SLASH_Fo, - clang::driver::options::OPT__SLASH_Fe, - clang::driver::options::OPT__SLASH_Fd, - clang::driver::options::OPT__SLASH_FA, - clang::driver::options::OPT__SLASH_Fa, - clang::driver::options::OPT__SLASH_Fi, - clang::driver::options::OPT__SLASH_FR, - clang::driver::options::OPT__SLASH_Fr, - clang::driver::options::OPT__SLASH_Fm, - clang::driver::options::OPT__SLASH_Fx, + clang::options::OPT_o, + clang::options::OPT__SLASH_o, + clang::options::OPT__SLASH_Fo, + clang::options::OPT__SLASH_Fe, + clang::options::OPT__SLASH_Fd, + clang::options::OPT__SLASH_FA, + clang::options::OPT__SLASH_Fa, + clang::options::OPT__SLASH_Fi, + clang::options::OPT__SLASH_FR, + clang::options::OPT__SLASH_Fr, + clang::options::OPT__SLASH_Fm, + clang::options::OPT__SLASH_Fx, #endif - // clang::driver::options::OPT__SLASH_TP - // clang::driver::options::OPT__SLASH_Tp - // clang::driver::options::OPT__SLASH_TC - // clang::driver::options::OPT__SLASH_Tc + // clang::options::OPT__SLASH_TP + // clang::options::OPT__SLASH_Tp + // clang::options::OPT__SLASH_TC + // clang::options::OPT__SLASH_Tc )) { return false; @@ -441,9 +441,9 @@ adjustCommandLine( // Clang option. This will discard any options that // affect warnings, are ignored, or turn warnings into // errors. - llvm::opt::OptTable const& opts_table = clang::driver::getDriverOptTable(); + llvm::opt::OptTable const& opts_table = clang::getDriverOptTable(); llvm::opt::Visibility visibility(is_clang_cl ? - clang::driver::options::CLOption : clang::driver::options::ClangOption); + clang::options::CLOption : clang::options::ClangOption); unsigned idx = 1; while (idx < cmdline.size()) { diff --git a/src/test/TestArgs.cpp b/src/test/TestArgs.cpp index 3dabba75f9..bf5d95b198 100644 --- a/src/test/TestArgs.cpp +++ b/src/test/TestArgs.cpp @@ -79,12 +79,12 @@ hideForeignOptions() const for (auto optionMap = llvm::cl::getRegisteredOptions(); auto& opt : optionMap) { - if (std::ranges::find(ours, opt.getValue()) != ours.end()) + if (std::ranges::find(ours, opt.second) != ours.end()) { - opt.getValue()->setHiddenFlag(llvm::cl::NotHidden); + opt.second->setHiddenFlag(llvm::cl::NotHidden); } else { - opt.getValue()->setHiddenFlag(llvm::cl::ReallyHidden); + opt.second->setHiddenFlag(llvm::cl::ReallyHidden); } } } diff --git a/src/tool/ToolArgs.cpp b/src/tool/ToolArgs.cpp index 229ea87f01..39b6e819b5 100644 --- a/src/tool/ToolArgs.cpp +++ b/src/tool/ToolArgs.cpp @@ -48,8 +48,8 @@ hideForeignOptions() auto optionMap = llvm::cl::getRegisteredOptions(); for(auto& opt : optionMap) { - opt.getValue()->setHiddenFlag( - std::ranges::find(oursOptions, opt.getValue()) != oursOptions.end() ? + opt.second->setHiddenFlag( + std::ranges::find(oursOptions, opt.second) != oursOptions.end() ? llvm::cl::NotHidden : llvm::cl::ReallyHidden); } diff --git a/test-files/golden-tests/javadoc/copydoc/param-types.xml b/test-files/golden-tests/javadoc/copydoc/param-types.xml index f63c1e6365..6f4f41fcfd 100644 --- a/test-files/golden-tests/javadoc/copydoc/param-types.xml +++ b/test-files/golden-tests/javadoc/copydoc/param-types.xml @@ -1328,7 +1328,7 @@ param-types.cpp param-types.cpp - 91 + 90 1 1 @@ -1552,7 +1552,7 @@ param-types.cpp param-types.cpp - 91 + 90 1 1 diff --git a/test-files/golden-tests/symbols/concept/requires-clause.xml b/test-files/golden-tests/symbols/concept/requires-clause.xml index ed2c11e5d2..5a24948122 100644 --- a/test-files/golden-tests/symbols/concept/requires-clause.xml +++ b/test-files/golden-tests/symbols/concept/requires-clause.xml @@ -8,8 +8,8 @@ //////////////////////////8= regular - kkH6jADpFuYV65W9qUpVvq20Cgs= - EBNjyD4nMb0o/Bpkztrno/0a+tA= + hK3x73/Ae4MeCnzwbIjl8ymPv3I= + bRI9ggyD7onLd/oP8QmcF0upggA= PmhXjCXUJYVnj/a7NhfjjSesyp0= 9JfSlZfEThLXiWTez3b8eT/AbZU= @@ -25,7 +25,7 @@ record - kkH6jADpFuYV65W9qUpVvq20Cgs= + hK3x73/Ae4MeCnzwbIjl8ymPv3I= regular //////////////////////////8= struct @@ -57,7 +57,7 @@ record - EBNjyD4nMb0o/Bpkztrno/0a+tA= + bRI9ggyD7onLd/oP8QmcF0upggA= regular //////////////////////////8= struct @@ -147,9 +147,9 @@ regular //////////////////////////8= normal - 3F1b/mMBJYuoAGH+KuNmPxsA7RI= - 9NLZvQibBhjkBj1mm77OINck29I= - vT2JiCSXaImfhLVMvGYQZ34RgLQ= + dMn9/neX/a+6Xf4Zj/trXAbJ3eE= + E8bM7kQ8fDu6ewzy2w4PlX3glWk= + waZu2K22py8SyW2+N49MTmkmhv8= identifier @@ -168,7 +168,7 @@ function - 3F1b/mMBJYuoAGH+KuNmPxsA7RI= + dMn9/neX/a+6Xf4Zj/trXAbJ3eE= regular //////////////////////////8= @@ -198,7 +198,7 @@ function - 9NLZvQibBhjkBj1mm77OINck29I= + E8bM7kQ8fDu6ewzy2w4PlX3glWk= regular //////////////////////////8= @@ -228,7 +228,7 @@ function - vT2JiCSXaImfhLVMvGYQZ34RgLQ= + waZu2K22py8SyW2+N49MTmkmhv8= regular //////////////////////////8= diff --git a/test-files/golden-tests/symbols/function/auto.xml b/test-files/golden-tests/symbols/function/auto.xml index 5d346ae6bf..fc11f081bf 100644 --- a/test-files/golden-tests/symbols/function/auto.xml +++ b/test-files/golden-tests/symbols/function/auto.xml @@ -17,7 +17,7 @@ auto.cpp auto.cpp - 7 + 6 1 1 diff --git a/test-files/golden-tests/symbols/function/function-template.xml b/test-files/golden-tests/symbols/function/function-template.xml index 86768c7dfd..1e9df9b6d7 100644 --- a/test-files/golden-tests/symbols/function/function-template.xml +++ b/test-files/golden-tests/symbols/function/function-template.xml @@ -315,7 +315,7 @@ function-template.cpp function-template.cpp 22 - 6 + 1 function @@ -346,7 +346,7 @@ function-template.cpp function-template.cpp 24 - 6 + 1 function diff --git a/test-files/golden-tests/symbols/record/dtor-overloads.xml b/test-files/golden-tests/symbols/record/dtor-overloads.xml index 017f166557..5d96e30f8f 100644 --- a/test-files/golden-tests/symbols/record/dtor-overloads.xml +++ b/test-files/golden-tests/symbols/record/dtor-overloads.xml @@ -73,8 +73,8 @@ destructor - x5T8zVxBVmg1ModTwgEAXcXWKDg= - uiH+ZSS+bYC5efGHgbdzlzXaV/o= + 3rUOGQX7PF0sqknq7fJH2ekGT1c= + BOrpr6vl5NDQn5Gj3sEhohxrWPU= identifier @@ -93,7 +93,7 @@ function - x5T8zVxBVmg1ModTwgEAXcXWKDg= + 3rUOGQX7PF0sqknq7fJH2ekGT1c= regular 5tUSuMtQqtYE49jBjSYSWp0DJAM= @@ -126,7 +126,7 @@ function - uiH+ZSS+bYC5efGHgbdzlzXaV/o= + BOrpr6vl5NDQn5Gj3sEhohxrWPU= regular 5tUSuMtQqtYE49jBjSYSWp0DJAM= diff --git a/third-party/recipes/llvm.json b/third-party/recipes/llvm.json index 1085723d71..d26e21358f 100644 --- a/third-party/recipes/llvm.json +++ b/third-party/recipes/llvm.json @@ -1,12 +1,12 @@ { "name": "llvm", - "version": "dc4cef81d47c7bc4a3c4d58fbacf8a6359683fae", + "version": "77e43ec11cd8fbe1de491118b54de9bba94510a8", "tags": [], "package_root_var": "LLVM_ROOT", "source": { "type": "archive", - "url": "https://github.com/llvm/llvm-project/archive/dc4cef81d47c7bc4a3c4d58fbacf8a6359683fae.zip", - "commit": "dc4cef81d47c7bc4a3c4d58fbacf8a6359683fae" + "url": "https://github.com/llvm/llvm-project/archive/77e43ec11cd8fbe1de491118b54de9bba94510a8.zip", + "commit": "77e43ec11cd8fbe1de491118b54de9bba94510a8" }, "dependencies": [], "build_type": "Release", diff --git a/util/bootstrap/tests/test_cache_keys.py b/util/bootstrap/tests/test_cache_keys.py index 8e2ee440a1..9fbb4f4620 100644 --- a/util/bootstrap/tests/test_cache_keys.py +++ b/util/bootstrap/tests/test_cache_keys.py @@ -24,7 +24,7 @@ ) -LLVM_HASH = "dc4cef81d47c7bc4a3c4d58fbacf8a6359683fae" +LLVM_HASH = "77e43ec11cd8fbe1de491118b54de9bba94510a8" class TestGenerateCacheKey(unittest.TestCase): @@ -38,7 +38,7 @@ def test_basic_release_key(self): build_type="Release", os_key="ubuntu:24.04", ) - self.assertEqual(key, "llvm-dc4cef8-release-ubuntu-24.04") + self.assertEqual(key, "llvm-77e43ec-release-ubuntu-24.04") def test_debug_build_type(self): """Build type should be lowercased.""" @@ -48,7 +48,7 @@ def test_debug_build_type(self): build_type="Debug", os_key="macos-15", ) - self.assertEqual(key, "llvm-dc4cef8-debug-macos-15") + self.assertEqual(key, "llvm-77e43ec-debug-macos-15") def test_relwithdebinfo_build_type(self): """RelWithDebInfo should be lowercased.""" @@ -58,7 +58,7 @@ def test_relwithdebinfo_build_type(self): build_type="RelWithDebInfo", os_key="ubuntu:24.04", ) - self.assertEqual(key, "llvm-dc4cef8-relwithdebinfo-ubuntu-24.04") + self.assertEqual(key, "llvm-77e43ec-relwithdebinfo-ubuntu-24.04") def test_colon_replaced_in_os_key(self): """Colons in os-key should be replaced with hyphens.""" @@ -89,7 +89,7 @@ def test_windows_os_key(self): build_type="Release", os_key="windows-2022", ) - self.assertEqual(key, "llvm-dc4cef8-release-windows-2022") + self.assertEqual(key, "llvm-77e43ec-release-windows-2022") def test_hash_truncated_to_7_chars(self): """Only first 7 characters of hash should be used.""" @@ -99,9 +99,9 @@ def test_hash_truncated_to_7_chars(self): build_type="Release", os_key="ubuntu:24.04", ) - self.assertIn("dc4cef8", key) + self.assertIn("77e43ec", key) # Full hash should NOT appear - self.assertNotIn("dc4cef81", key) + self.assertNotIn("77e43ec1", key) def test_clang_asan_suffix(self): """Clang + ASan should add -clang-{version}-ASan suffix.""" @@ -115,7 +115,7 @@ def test_clang_asan_suffix(self): sanitizer="address", ) self.assertEqual( - key, "llvm-dc4cef8-release-ubuntu-24.04-clang-19-ASan" + key, "llvm-77e43ec-release-ubuntu-24.04-clang-19-ASan" ) def test_clang_asan_short_name(self): @@ -130,7 +130,7 @@ def test_clang_asan_short_name(self): sanitizer="asan", ) self.assertEqual( - key, "llvm-dc4cef8-release-ubuntu-24.04-clang-19-ASan" + key, "llvm-77e43ec-release-ubuntu-24.04-clang-19-ASan" ) def test_clang_msan_suffix(self): @@ -146,7 +146,7 @@ def test_clang_msan_suffix(self): ) # MSan has no archive-sanitizer-str in CI template, so empty after last dash self.assertEqual( - key, "llvm-dc4cef8-release-ubuntu-24.04-clang-19-" + key, "llvm-77e43ec-release-ubuntu-24.04-clang-19-" ) def test_gcc_asan_no_suffix(self): @@ -160,7 +160,7 @@ def test_gcc_asan_no_suffix(self): compiler_version="14", sanitizer="address", ) - self.assertEqual(key, "llvm-dc4cef8-release-ubuntu-24.04") + self.assertEqual(key, "llvm-77e43ec-release-ubuntu-24.04") def test_clang_ubsan_no_suffix(self): """Clang + UBSan should NOT add suffix (only asan/msan trigger it).""" @@ -173,7 +173,7 @@ def test_clang_ubsan_no_suffix(self): compiler_version="19", sanitizer="undefined", ) - self.assertEqual(key, "llvm-dc4cef8-release-ubuntu-24.04") + self.assertEqual(key, "llvm-77e43ec-release-ubuntu-24.04") def test_clang_tsan_no_suffix(self): """Clang + TSan should NOT add suffix.""" @@ -186,7 +186,7 @@ def test_clang_tsan_no_suffix(self): compiler_version="19", sanitizer="thread", ) - self.assertEqual(key, "llvm-dc4cef8-release-ubuntu-24.04") + self.assertEqual(key, "llvm-77e43ec-release-ubuntu-24.04") def test_no_compiler_no_suffix(self): """No compiler specified should produce no suffix.""" @@ -197,7 +197,7 @@ def test_no_compiler_no_suffix(self): os_key="ubuntu:24.04", sanitizer="address", ) - self.assertEqual(key, "llvm-dc4cef8-release-ubuntu-24.04") + self.assertEqual(key, "llvm-77e43ec-release-ubuntu-24.04") def test_no_sanitizer_no_suffix(self): """No sanitizer should produce no suffix even with clang.""" @@ -209,7 +209,7 @@ def test_no_sanitizer_no_suffix(self): compiler="clang", compiler_version="19", ) - self.assertEqual(key, "llvm-dc4cef8-release-ubuntu-24.04") + self.assertEqual(key, "llvm-77e43ec-release-ubuntu-24.04") def test_non_llvm_recipe(self): """Cache keys should work for any recipe, not just llvm.""" @@ -308,7 +308,7 @@ def test_release_ubuntu_gcc(self): compiler_version="14", ) # gcc builds never get the compiler suffix - self.assertEqual(key, "llvm-dc4cef8-release-ubuntu-24.04") + self.assertEqual(key, "llvm-77e43ec-release-ubuntu-24.04") def test_release_ubuntu_clang_asan(self): """Clang ASan release on ubuntu:24.04.""" @@ -322,7 +322,7 @@ def test_release_ubuntu_clang_asan(self): sanitizer="address", ) self.assertEqual( - key, "llvm-dc4cef8-release-ubuntu-24.04-clang-19-ASan" + key, "llvm-77e43ec-release-ubuntu-24.04-clang-19-ASan" ) def test_release_windows(self): @@ -335,7 +335,7 @@ def test_release_windows(self): compiler="msvc", compiler_version="14.42", ) - self.assertEqual(key, "llvm-dc4cef8-release-windows-2022") + self.assertEqual(key, "llvm-77e43ec-release-windows-2022") def test_release_macos(self): """apple-clang release on macos-15.""" @@ -348,7 +348,7 @@ def test_release_macos(self): compiler_version="16", ) # apple-clang never gets suffix (only "clang" does) - self.assertEqual(key, "llvm-dc4cef8-release-macos-15") + self.assertEqual(key, "llvm-77e43ec-release-macos-15") if __name__ == "__main__": From d9dd6a82d15d6b8631f80e96e652c937090dc14b Mon Sep 17 00:00:00 2001 From: Gennaro Prota Date: Wed, 8 Jul 2026 12:04:49 +0200 Subject: [PATCH 2/3] ci: build the instrumented libc++ beside the plain one The MSan and ASan jobs build MrDocs against a sanitizer-instrumented libc++. MSan requires that, since it reports false positives on any uninstrumented code; ASan doesn't require it, but is more effective with it, through libc++'s container-overflow annotations. The pinned libc++ contains a self-guard (added upstream in llvm/llvm-project#168955) that errors out when an ASan-instrumented libc++ is parsed without ASan, and MrDocs parses libc++ with normal flags for the libcxx.cpp golden. So an instrumented libc++ can no longer double as the one MrDocs reads. Thus, install both an instrumented and an uninstrumented libc++. Specifically, install the instrumented libc++ into a prefix separate from the main LLVM build's plain libc++; `needs_libcxx_runtimes` still selects it for Clang + ASan/MSan. MrDocs links the instrumented library through `libcxx_runtime_flags` but parses the plain headers the main build leaves at the LLVM install prefix. Also, move Clang ASan and MSan to the latest Clang, since building the pinned libc++ from source needs a recent one. --- .github/workflows/ci-matrix.yml | 3 +-- util/bootstrap/src/installer.py | 8 ++++---- util/bootstrap/src/recipes/builder.py | 25 +++++++++++++++++++------ util/bootstrap/tests/test_builder.py | 24 ++++++++++++++++++++++++ util/bootstrap/tests/test_installer.py | 18 +++++++++++------- 5 files changed, 59 insertions(+), 19 deletions(-) diff --git a/.github/workflows/ci-matrix.yml b/.github/workflows/ci-matrix.yml index cf5a4c2f74..29ab6789d9 100644 --- a/.github/workflows/ci-matrix.yml +++ b/.github/workflows/ci-matrix.yml @@ -43,10 +43,9 @@ jobs: main-entry-factors: | clang Coverage latest-factors: | - clang UBSan + clang UBSan MSan ASan factors: | gcc UBSan - clang ASan MSan apple-clang UBSan ASan build-types: | gcc: Release diff --git a/util/bootstrap/src/installer.py b/util/bootstrap/src/installer.py index 28ed19ab09..f205fffb4b 100644 --- a/util/bootstrap/src/installer.py +++ b/util/bootstrap/src/installer.py @@ -55,6 +55,7 @@ def _shquote(s: str) -> str: build_libcxx_runtimes, needs_libcxx_runtimes, libcxx_runtime_flags, + sanitized_libcxx_prefix, write_recipe_stamp, recipe_stamp_path, is_recipe_up_to_date, @@ -484,7 +485,8 @@ def install_dependencies(self): # even on cache hit, since downstream builds need them. if recipe.name == "llvm": if needs_libcxx_runtimes(self.options.sanitizer, compiler_id): - rt_flags = libcxx_runtime_flags(recipe.install_dir) + rt_flags = libcxx_runtime_flags( + sanitized_libcxx_prefix(recipe.install_dir)) self._libcxx_cxxflags = rt_flags["cxxflags"] self._libcxx_ldflags = rt_flags["ldflags"] self.options.cxxflags = (self.options.cxxflags + " " + rt_flags["cxxflags"]).strip() @@ -550,10 +552,8 @@ def install_dependencies(self): self.options.debug, self.env, self.ui, + install_prefix=sanitized_libcxx_prefix(recipe.install_dir), ) - # Disable runtimes in the main LLVM build so it doesn't - # overwrite the instrumented ones we just built - extra_cmake_options = ["-DLLVM_ENABLE_RUNTIMES="] self._dry_comment(f"Build and install {recipe.name}") build_recipe( diff --git a/util/bootstrap/src/recipes/builder.py b/util/bootstrap/src/recipes/builder.py index 9890f2fcd6..3e6269c599 100644 --- a/util/bootstrap/src/recipes/builder.py +++ b/util/bootstrap/src/recipes/builder.py @@ -52,6 +52,17 @@ def needs_libcxx_runtimes(sanitizer: str, compiler_id: str) -> bool: return True +def sanitized_libcxx_prefix(llvm_install_dir: str) -> str: + """ + Return the install prefix for the sanitizer-instrumented libc++. + + It sits beside the plain libc++ under the LLVM install directory, so the + instrumented runtime is cached with LLVM while the plain headers at + /include/c++/v1 stay the ones MrDocs parses. + """ + return os.path.join(llvm_install_dir, "sanitized-libcxx") + + def build_libcxx_runtimes( recipe: Recipe, cc: str = "", @@ -62,16 +73,15 @@ def build_libcxx_runtimes( debug: bool = False, env: Optional[dict] = None, ui: Optional[TextUI] = None, + install_prefix: str = "", ): """ Build libc++/libc++abi runtimes with sanitizer instrumentation. - This builds instrumented runtimes before the main LLVM build so that - downstream builds use sanitizer-instrumented standard libraries. - The runtimes are built from /runtimes and installed into - the LLVM install prefix. The runtimes build directory is cleaned up - after installation. + install_prefix, a location separate from the plain libc++ so that MrDocs + links against the instrumented library while still parsing the plain + headers. The runtimes build directory is cleaned up after installation. Args: recipe: The LLVM recipe (provides source_dir, build_dir, install_dir). @@ -83,6 +93,8 @@ def build_libcxx_runtimes( debug: If True, show debug output. env: Environment variables for commands. ui: TextUI instance for output. + install_prefix: Where to install the runtimes. Defaults to the LLVM + install directory. """ if ui is None: ui = get_default_ui() @@ -104,7 +116,8 @@ def build_libcxx_runtimes( runtimes_src = os.path.join(recipe.source_dir, "runtimes") runtimes_build = recipe.build_dir + "-runtimes" - install_prefix = recipe.install_dir + if not install_prefix: + install_prefix = recipe.install_dir # Determine runtimes to build runtimes = "libcxx;libcxxabi" diff --git a/util/bootstrap/tests/test_builder.py b/util/bootstrap/tests/test_builder.py index da0ddc025c..dc9eb59a51 100644 --- a/util/bootstrap/tests/test_builder.py +++ b/util/bootstrap/tests/test_builder.py @@ -5,6 +5,7 @@ # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception # # Copyright (c) 2025 Alan de Freitas (alandefreitas@gmail.com) +# Copyright (c) 2026 Gennaro Prota (gennaro.prota@gmail.com) # # Official repository: https://github.com/cppalliance/mrdocs # @@ -290,6 +291,29 @@ def test_runtimes_install_prefix(self, mock_which, mock_ensure, mock_run, mock_r self.assertTrue(len(install_prefix) > 0) self.assertIn(recipe.install_dir, install_prefix[0]) + @patch("src.recipes.builder.remove_dir") + @patch("src.recipes.builder.run_cmd") + @patch("src.recipes.builder.ensure_dir") + @patch("shutil.which", return_value="/usr/bin/cmake") + def test_runtimes_custom_install_prefix(self, mock_which, mock_ensure, mock_run, mock_rm): + """A given install_prefix should override the default LLVM install dir.""" + from src.recipes.builder import build_libcxx_runtimes + recipe = _make_recipe(name="llvm") + build_libcxx_runtimes( + recipe, sanitizer="address", + install_prefix="/install/testlib/sanitized-libcxx", + ) + cfg_cmd = mock_run.call_args_list[0][0][0] + install_prefix = [a for a in cfg_cmd if "CMAKE_INSTALL_PREFIX" in a] + self.assertIn("/install/testlib/sanitized-libcxx", install_prefix[0]) + + def test_sanitized_libcxx_prefix_is_a_distinct_subdir(self): + """The instrumented libc++ prefix is a distinct dir beside the plain one.""" + from src.recipes.builder import sanitized_libcxx_prefix + prefix = sanitized_libcxx_prefix("/opt/llvm") + self.assertTrue(prefix.endswith("sanitized-libcxx")) + self.assertNotEqual(prefix, "/opt/llvm") + @patch("src.recipes.builder.remove_dir") @patch("src.recipes.builder.run_cmd") @patch("src.recipes.builder.ensure_dir") diff --git a/util/bootstrap/tests/test_installer.py b/util/bootstrap/tests/test_installer.py index ad12a18cc5..1698ddd782 100644 --- a/util/bootstrap/tests/test_installer.py +++ b/util/bootstrap/tests/test_installer.py @@ -661,13 +661,14 @@ def test_install_dependencies_tracks_rebuilt_recipes( @patch("src.installer.fetch_recipe_source") @patch("src.installer.topo_sort_recipes", side_effect=lambda x: x) @patch("src.installer.load_recipe_files") - def test_install_dependencies_disables_main_llvm_runtimes_when_libcxx_built( + def test_install_dependencies_builds_instrumented_libcxx_beside_plain( self, mock_load, mock_topo, mock_fetch, mock_patch, mock_build, mock_stamp, mock_flags, mock_needs, mock_libcxx ): - """LLVM + clang + ASan/MSan should pass -DLLVM_ENABLE_RUNTIMES= to the - main LLVM build via extra_cmake_options, so the instrumented libc++ - we just built isn't overwritten.""" + """LLVM + clang + ASan/MSan should build the instrumented libc++ into a + separate prefix and leave the main build's plain libc++ in place (no + -DLLVM_ENABLE_RUNTIMES= override), so MrDocs parses the plain headers.""" + from src.recipes.builder import sanitized_libcxx_prefix recipe = self._make_recipe("llvm") mock_load.return_value = [recipe] @@ -676,10 +677,13 @@ def test_install_dependencies_disables_main_llvm_runtimes_when_libcxx_built( inst.install_dependencies() mock_libcxx.assert_called_once() - # Find the extra_cmake_options arg in the build_recipe call - # (positional, between ldflags and force). + self.assertEqual( + mock_libcxx.call_args.kwargs["install_prefix"], + sanitized_libcxx_prefix(recipe.install_dir), + ) build_args = mock_build.call_args[0] - self.assertIn(["-DLLVM_ENABLE_RUNTIMES="], build_args) + self.assertIn(None, build_args) + self.assertNotIn(["-DLLVM_ENABLE_RUNTIMES="], build_args) @patch("src.installer.needs_libcxx_runtimes", return_value=False) @patch("src.installer.write_recipe_stamp") From 6418b8239f0bbfa9db8037a4783c372bf7307ea3 Mon Sep 17 00:00:00 2001 From: Gennaro Prota Date: Tue, 7 Jul 2026 08:01:22 +0200 Subject: [PATCH 3/3] ci: drop third-party profraw from the coverage merge The coverage step globs build/ for *.profraw, which after the bump also holds the extracted LLVM source tree. That tree ships llvm-profdata test fixtures in an older raw-profile version that llvm-profdata rejects, failing the merge. Delete build/third-party profraw first; MrDocs' own profraw live elsewhere under build/. --- .github/workflows/ci-build.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.github/workflows/ci-build.yml b/.github/workflows/ci-build.yml index 10950b2243..bd1f66a200 100644 --- a/.github/workflows/ci-build.yml +++ b/.github/workflows/ci-build.yml @@ -156,6 +156,15 @@ jobs: build-dir: build github-token: ${{ secrets.GITHUB_TOKEN }} + # The coverage step scans build/ for *.profraw, which also sweeps up + # the committed *.profraw test fixtures in LLVM's extracted source + # tree; their older format fails the llvm-profdata merge. The + # process-coverage action can't exclude a subdirectory from that + # scan, so drop those foreign files first. + - name: Prune third-party profraw + if: matrix.coverage + run: find build/third-party -name '*.profraw' -delete + - name: Process Coverage id: process-coverage if: matrix.coverage