[Clang][Modules] explicit specifier lost on template conversion operators after PCM serialization
- Dominant language
- LLVM
- Stars
- 40.5k
- Forks
- 18.7k
- PR merge metrics
- PR metrics pending
Description
## Description
Template conversion operators declared `explicit` lose their `explicit` specifier after PCM serialization/deserialization through C++ modules. This causes `std::is_convertible_v` to return `true` for conversions that should only be available via `static_cast`.
The bug only affects a specific subset of conversions: cross-direction, same-constness conversions in a bidirectional iterator implementation. Other conversions (same-direction, or cross-direction+const) correctly retain their `explicit` specifier.
## Precise diagnosis
The conversion operators have 8 template parameters with complex SFINAE conditions via `std::enable_if_t`. After importing through a deep module chain (10+ PCM layers), the `explicit` specifier is lost on specific overloads:
| Conversion | is_convertible_v | static_cast | Status |
|---|---|---|---|
| FwdNC -> FwdC (same direction) | false | works | OK (explicit retained) |
| FwdNC -> RevNC (cross direction) | **true** | works | **BUG: explicit lost** |
| FwdNC -> RevC (cross direction + const) | false | works | OK (explicit retained) |
| RevNC -> RevC (same direction) | false | works | OK (explicit retained) |
The `explicit` specifier IS lost (not a missing conversion -- `static_cast` still works, but implicit conversion is now allowed). This pattern repeats symmetrically for all non-const cross-direction conversions (Rev->Fwd also affected).
## Source location
The conversion operators are at:
```cpp
// type/integer/fundamental/offset/abstract/iterator/implementation.hpp:1364
template
explicit operator NewSelf() { /* ... */ }
```
## Reproducer status
A self-contained minimal reproducer has not been achieved despite extensive attempts. The bug requires the full project module graph depth (10+ PCM layers through a CRTP/Aspect/PID/Context/Stack injection chain).
Tested and confirmed NOT to trigger the bug:
- Single module with same template structure (up to 8 params)
- Module partition -> primary re-export chain with same structure
- 2-level module re-export chain with same structure
- Adding CRTP Self, BaseInt, StaticCastable chain
- Various combinations of trait lookup patterns
The bug specifically requires the depth of the module serialization chain, suggesting the `explicit` specifier is corrupted or dropped during multi-layer PCM serialization/deserialization.
## How to verify
With the project, a diagnostic test confirms:
```cpp
// is_convertible_v should be false (explicit prevents implicit conversion)
// static_cast should work (conversion exists)
static_assert(!std::is_convertible_v); // FAILS: is_convertible_v=true
static_assert(std::is_constructible_v); // OK: static_cast works
```
## Environment
- **Clang version**: 23.0.0git (commit 3392ec8cf17ce90fe0859ae31e86cf48f8fa185d)
- **OS**: macOS 15.4 (Darwin 25.3.0), ARM64
Contributor guide
Assessment
This issue has not been assessed yet.