diff --git a/.drone.star b/.drone.star index ed0f56fc..c0cfa0e3 100644 --- a/.drone.star +++ b/.drone.star @@ -41,8 +41,11 @@ def main(ctx): compiler='gcc-12', cxxstd='17,20', os='ubuntu-22.04'), job(name='TSAN', tsan=True, compiler='gcc-12', cxxstd='17,20', os='ubuntu-22.04'), - job(name='Clang 14 w/ sanitizers', asan=True, ubsan=True, - compiler='clang-14', cxxstd='17', os='ubuntu-22.04'), + # clang >= 18 is required here: older ASan runtimes crash randomly under + # the high-entropy ASLR of the CI runners' kernels. + # See https://github.com/google/sanitizers/issues/1716 + job(name='Clang 18 w/ sanitizers', asan=True, ubsan=True, + compiler='clang-18', cxxstd='17', os='ubuntu-24.04'), job(name='Clang 11 libc++ w/ sanitizers', asan=True, ubsan=True, # libc++-11 is the latest working with ASAN: https://github.com/llvm/llvm-project/issues/59432 compiler='clang-11', cxxstd='17', os='ubuntu-20.04', stdlib='libc++', install='libc++-11-dev libc++abi-11-dev'), diff --git a/.github/workflows/fedora.yml b/.github/workflows/fedora.yml index 3822f5c7..bd4bc1ed 100644 --- a/.github/workflows/fedora.yml +++ b/.github/workflows/fedora.yml @@ -26,8 +26,26 @@ jobs: cxx_std: 23 - cxx: clang-16 cxx_std: 23 + # Test the distro's current clang against the current libstdc++ on + # fedora:latest; the versioned-clang jobs are pinned to Fedora 43 + # (see 'container:' below). + include: + - cxx: clang + cxx_std: 17 + container: docker.io/library/fedora:latest + - cxx: clang + cxx_std: 20 + container: docker.io/library/fedora:latest + - cxx: clang + cxx_std: 23 + container: docker.io/library/fedora:latest runs-on: ubuntu-latest - container: docker.io/library/fedora:latest + # Fedora 44 ships libstdc++ from GCC 16, whose + # (hidden-friend __normal_iterator operators) does not compile with + # clang < 19: "member access into incomplete type". Pin the versioned + # clang jobs (15-18) to Fedora 43, which ships libstdc++ 15. Revisit + # the pin when the oldest tested clang is >= 19. + container: ${{ matrix.container || 'docker.io/library/fedora:43' }} steps: - name: Install Dependencies @@ -36,6 +54,10 @@ jobs: gcc) dnf install -y gcc-c++ ;; + clang) + # distro's current clang (used on fedora:latest) + dnf install -y clang gcc-c++ + ;; clang-16) # the llvm-compat-packages copr lacks clang-16 for some reason dnf install -y 'dnf-command(copr)' @@ -71,6 +93,10 @@ jobs: export CC=gcc export CXX=g++ ;; + clang) + export CC=clang + export CXX=clang++ + ;; clang-*) export CC="${{ matrix.cxx }}" export CXX="$(echo "${{ matrix.cxx }}" | sed 's/clang/clang++/')" diff --git a/include/boost/parser/detail/text/detail/all_t.hpp b/include/boost/parser/detail/text/detail/all_t.hpp index 7e65b607..b5b7e448 100644 --- a/include/boost/parser/detail/text/detail/all_t.hpp +++ b/include/boost/parser/detail/text/detail/all_t.hpp @@ -11,7 +11,8 @@ #include #include -#if BOOST_PARSER_USE_CONCEPTS +#if BOOST_PARSER_USE_CONCEPTS || \ + (defined(__cpp_lib_concepts) && defined(__cpp_lib_ranges)) #include #endif @@ -41,8 +42,12 @@ namespace boost::parser::detail::text::detail { template constexpr bool view = +// __cpp_lib_concepts alone does not imply that std::ranges exists: Apple's +// libc++ (Xcode 13.4-15.0) and LLVM libc++ 14/15 define __cpp_lib_concepts +// but not __cpp_lib_ranges, and ship no (or incomplete) std::ranges. Fall +// back to the heuristic below unless the ranges library is really there. #if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS || \ - (defined(__cpp_lib_concepts) && \ + (defined(__cpp_lib_concepts) && defined(__cpp_lib_ranges) && \ (!defined(BOOST_PARSER_GCC) || 12 <= __GNUC__)) std::ranges::view #else diff --git a/include/boost/parser/subrange.hpp b/include/boost/parser/subrange.hpp index 84dd3b64..edebdfba 100644 --- a/include/boost/parser/subrange.hpp +++ b/include/boost/parser/subrange.hpp @@ -104,6 +104,28 @@ namespace std::ranges { true; } +#elif defined(__cpp_lib_concepts) && defined(__cpp_lib_ranges) + +// In C++20 builds that do not use the library's concepts code path (Clang +// 13-15, or BOOST_PARSER_DISABLE_CONCEPTS), detail::text::detail::all() +// still uses std::ranges::view to decide whether to wrap a range in a +// move-only owning_view. subrange is a borrowed view (a non-owning +// iterator/sentinel pair), so opt it in explicitly; otherwise ranges built +// on top of it (e.g. null_term(p) | as_utf16) become move-only and break +// copy-requiring code paths such as transform_replace. The +// __cpp_lib_ranges check matters: some libc++ versions (e.g. Apple's in +// Xcode 13.4-15.0) define __cpp_lib_concepts without shipping std::ranges, +// so neither enable_view nor std::ranges::view exists there. +#include + +namespace std::ranges { + template + inline constexpr bool enable_borrowed_range> = + true; + template + inline constexpr bool enable_view> = true; +} + #endif #endif