Skip to content

fix cxxmodule: export all CPOs unconditionally in fast_io module wrapper#1277

Merged
trcrsired merged 1 commit into
cppfastio:nextfrom
SekaiArendelle:fix-cxxmodule
Jun 1, 2026
Merged

fix cxxmodule: export all CPOs unconditionally in fast_io module wrapper#1277
trcrsired merged 1 commit into
cppfastio:nextfrom
SekaiArendelle:fix-cxxmodule

Conversation

@SekaiArendelle
Copy link
Copy Markdown

fix(cxxmodule): export all CPOs unconditionally in fast_io module wrapper Several customization point objects (io_stream_ref_define, output_stream_ref_define, write_some_overflow_define, etc.) in cxxmodule/fast_io/fast_io_inc/core.inc were guarded by #if !defined(clang), under the assumption that clang supports finding non-exported names via ADL in module contexts. This assumption does not hold on clang 23 for x86_64-windows-gnu, where importing the fast_io module and calling println(u8c_stdout(), u8string) fails: static assertion failed due to requirement 'type_ok': some types are not printable for print on default C's stdout Without these exports, the requires clause on output_stream_ref (has_output_or_io_stream_ref_define) fails via SFINAE, causing print_freestanding_okay to fall through to the char-based hosted path, which cannot handle char8_t data. Remove the clang guard so CPOs are exported unconditionally. Also prefix all HTML AST node types with Html in pltxt2htm.cppm to match the upstream rename, and add missing table-node exports.

fix(cxxmodule): export all CPOs unconditionally in fast_io module wrapper
Several customization point objects (io_stream_ref_define, output_stream_ref_define, write_some_overflow_define, etc.) in cxxmodule/fast_io/fast_io_inc/core.inc were guarded by #if !defined(__clang__), under the assumption that clang supports finding non-exported names via ADL in module contexts. This assumption does not hold on clang 23 for x86_64-windows-gnu, where importing the fast_io module and calling println(u8c_stdout(), u8string) fails:
static assertion failed due to requirement 'type_ok':
some types are not printable for print on default C's stdout
Without these exports, the requires clause on output_stream_ref (has_output_or_io_stream_ref_define<T>) fails via SFINAE, causing print_freestanding_okay to fall through to the char-based hosted path, which cannot handle char8_t data.
Remove the clang guard so CPOs are exported unconditionally.
Also prefix all HTML AST node types with Html in pltxt2htm.cppm to
match the upstream rename, and add missing table-node exports.
@SekaiArendelle
Copy link
Copy Markdown
Author

Minimal reproduction for fast_io C++20 module CPO export issue on clang

Issue

When using fast_io as a C++20 module on clang (tested on clang 23 for
x86_64-windows-gnu, but likely affects other clang versions/targets too),
printing a u8string to u8c_stdout() fails:

error: static assertion failed due to requirement 'type_ok':
some types are not printable for print on default C's stdout

The static_assert is in fast_io_legacy_impl/io.h:91.

Root Cause

In cxxmodule/fast_io/fast_io_inc/core.inc (and other .inc files),
several customization point objects (CPOs) are guarded by
#if !defined(__clang__):

#if !defined(__clang__) // The gcc and msvc module does not support
                        // non-exported functions adl matching
using ::fast_io::io_stream_ref_define;
using ::fast_io::output_stream_ref_define;
using ::fast_io::write_some_overflow_define;
// ... many more CPOs ...
#endif

The comment assumes clang supports ADL matching non-exported names in
module contexts, so these exports are skipped for clang.

However, on clang 23 for x86_64-windows-gnu, this assumption is wrong.
Without these explicit using exports, the CPOs are not reachable from
importing TUs, so:

  1. println(u8c_stdout(), u8string) checks
    print_freestanding_okay<u8c_stdout_type, u8string>
  2. This calls output_stream_ref(declval<u8c_stdout_type>())
  3. output_stream_ref has a requires clause
    has_output_or_io_stream_ref_define<T> which needs ADL to find
    io_stream_ref_define
  4. ADL fails → output_stream_ref is not viable → the concept fails
  5. Falls through to the char-based hosted path
  6. print_freestanding_params_okay<char, u8string> fails because
    char != char8_tstatic_assert

Fix

Remove the #if !defined(__clang__) guard or add a version check so
that all CPOs are unconditionally exported:

// Before:
#if !defined(__clang__)
using ::fast_io::io_stream_ref_define;
// ...
#endif

// After (unconditional export):
using ::fast_io::io_stream_ref_define;
// ...

Files to fix

  • cxxmodule/fast_io/fast_io_inc/core.inc — the largest block of CPO
    exports guarded by #if !defined(__clang__)
  • cxxmodule/fast_io/fast_io_inc/legacy/c.inc — line 39–41:
    pread_some_bytes_underflow_define, pwrite_some_bytes_overflow_define
  • cxxmodule/fast_io/fast_io_inc/device.inc — various write_some_*
    and read_some_* CPOs

Tested Environment

  • OS: Windows (x86_64)
  • Compiler: clang 23.0.0git
    (target: x86_64-unknown-windows-gnu)
  • Build system: CMake 3.28+ with Ninja
  • C++ Standard: C++23

@SekaiArendelle
Copy link
Copy Markdown
Author

SekaiArendelle commented May 31, 2026

Based on https://eel.is/c++draft/basic.lookup.argdep:

Argument-dependent lookup finds all declarations of functions and function templates that …
are exported, are attached to a named module M, do not appear in the translation unit containing the point of the lookup, and have the same innermost enclosing non-inline namespace scope as a declaration of an associated entity attached to M.

@trcrsired trcrsired merged commit 547077c into cppfastio:next Jun 1, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants