diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2a3b30cde..da56fc205 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,6 +23,7 @@ jobs: matrix: rust: [nightly, beta, stable, 1.82.0, 1.77.0, 1.74.0, 1.73.0] os: [ubuntu] + flags: [''] include: - name: Cargo on macOS rust: nightly @@ -31,6 +32,18 @@ jobs: rust: nightly-x86_64-pc-windows-msvc os: windows flags: /EHsc + - name: C++14 + rust: nightly + os: ubuntu + flags: -std=c++14 + - name: C++17 + rust: nightly + os: ubuntu + flags: -std=c++17 + - name: C++20 + rust: nightly + os: ubuntu + flags: -std=c++20 env: CXXFLAGS: ${{matrix.flags}} RUSTFLAGS: --cfg deny_warnings -Dwarnings diff --git a/include/cxx.h b/include/cxx.h index 928f3d9c9..f0bf3f067 100644 --- a/include/cxx.h +++ b/include/cxx.h @@ -45,6 +45,13 @@ class String final { String(const char *, std::size_t); String(const char16_t *); String(const char16_t *, std::size_t); +#if __cplusplus >= 202002L + // `reinterpret_cast` of `const char*` into `const char8_t*` is dangerous, + // because `const char*` may point to non-UTF8. OTOH, conversion in the + // other direction seems safe. + String(const char8_t *s) : String(reinterpret_cast(s)) {} + String(const char8_t *s, std::size_t len) : String(reinterpret_cast(s), len) {} +#endif // Replace invalid Unicode data with the replacement character (U+FFFD). static String lossy(const std::string &) noexcept; diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index 2292914cd..674572136 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -878,7 +878,8 @@ extern "C" const char *cxx_run_test() noexcept { ASSERT(cstr == "foo"); ASSERT(other_cstr == "test"); - const char *utf8_literal = u8"Test string"; + // `u8"foo"` is `const char*` before, and `const char8_t*` after C++ 20, so using `auto`. + const auto *utf8_literal = u8"Test string"; const char16_t *utf16_literal = u"Test string"; rust::String utf8_rstring = utf8_literal; rust::String utf16_rstring = utf16_literal;