Skip to content

Stop accepting string<long double>#6099

Open
AlexGuteniev wants to merge 9 commits into
microsoft:mainfrom
AlexGuteniev:no-strings-attached
Open

Stop accepting string<long double>#6099
AlexGuteniev wants to merge 9 commits into
microsoft:mainfrom
AlexGuteniev:no-strings-attached

Conversation

@AlexGuteniev

@AlexGuteniev AlexGuteniev commented Feb 22, 2026

Copy link
Copy Markdown
Contributor

Very conservative step towards #5311.
Prohibit at least non-integer default char traits, with escape hatch.

🎈 Floating motivation

Out of all types that we should not have supported, floats are my primary concern, especially their interaction with vectorization. We do ban them along with other unexpected types in find_meow_of:

static_assert(is_unsigned_v<_Elem>, "Standard char_traits is only provided for char, wchar_t, char8_t, char16_t, "
"and char32_t. See N4988 [char.traits]. "
"Visual C++ accepts other unsigned integral types as an extension.");

But with find/rfind the effect would be strange. I have not tried, but I expect that the unsupported 80-bit long double would fail to compile with a strange error, but normal floats and doubles would compare bitwise, resulting incorrect results for nans and negative zeros.

This use case has never existed, so everything looks like we should ban it outright.

But while we're at it, let's ban all other non-standard things, except integers.

🔢 Integers

Getting rid of non-character integers, both signed and unsigned, turned out to be much more complicated.

Specifically with filesystem test, where it uses signed char and unsigned char, and expects path conversion to work,

Let's not do this for now.

👮‍♂️ Offenders

Dev10_860410_bitset_ctors starts very normal:

// test encoded character types (expected usages)
test_ntcts_constructibility_for_lengths<char, true>();
#ifdef __cpp_char8_t
test_ntcts_constructibility_for_lengths<char8_t, true>();
#endif // __cpp_char8_t
test_ntcts_constructibility_for_lengths<char16_t, true>();
test_ntcts_constructibility_for_lengths<char32_t, true>();
test_ntcts_constructibility_for_lengths<wchar_t, true>();

Then it tests weird types:

// test scalar types
test_ntcts_constructibility_for_lengths<int, true>();
test_ntcts_constructibility_for_lengths<char*, true>();
test_ntcts_constructibility_for_lengths<void (*)(), true>();
test_ntcts_constructibility_for_lengths<unscoped_enum, true>();
test_ntcts_constructibility_for_lengths<scoped_enum, true>();
test_ntcts_constructibility_for_lengths<int missing_trivial_default_ctor::*, true>();
test_ntcts_constructibility_for_lengths<void (missing_standard_layout::*)() const&, true>();

Culminating in this:

// test worse class types
test_ntcts_constructibility_for_lengths<string, false>();
test_ntcts_constructibility_for_lengths<invalid_argument, false>();

Looks like this is not supported. bitset constructor is defined to work as if with string or string_view, so if no explicit traits are passed, these are the default traits, that should not be defined for these odd types.

In addition P0980R1_constexpr_strings tests for weirdness, but a more modest one:

template <class CharType>
struct CharLikeType {
constexpr CharLikeType() = default;
constexpr CharLikeType(CharType cc) : c(cc) {}
CharType c;
};

basic_string<CharLikeType<CharType>> bs{CharType{'x'}};

I've added escape hatches to both tests matrices.

@StephanTLavavej StephanTLavavej added the enhancement Something can be improved label Feb 22, 2026
@StephanTLavavej

Copy link
Copy Markdown
Member

For built-in types, I'm fine with banning non-integers. There's no reason to support that complexity. But I don't think we can ban user-defined types, because users are allowed to provide a specialization of char_traits<MyCharLikeType>, right?

@AlexGuteniev

Copy link
Copy Markdown
Contributor Author

But I don't think we can ban user-defined types, because users are allowed to provide a specialization of char_traits<MyCharLikeType>, right?

Yes you are right>

@AlexGuteniev
AlexGuteniev marked this pull request as draft February 22, 2026 12:58
@AlexGuteniev
AlexGuteniev marked this pull request as ready for review February 22, 2026 13:02
@AlexGuteniev

Copy link
Copy Markdown
Contributor Author

But I don't think we can ban user-defined types, because users are allowed to provide a specialization of char_traits<MyCharLikeType>, right?

However!

We ban directly in the primary char_traits template. Any specialization will bypass this ban.

We can additionally ban floats outside, disabling users to provide their own char_traits<long double> specialization, but I dont believe in pursuing basic_string<long double> with this level of persistence.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR tightens the MSVC STL extension behavior around std::char_traits by rejecting instantiations for non-integral element types (e.g. basic_string<long double>), with a test-only escape hatch to keep existing “exotic type” test matrices working.

Changes:

  • Add a static_assert in the primary std::char_traits<T> template to reject non-integral T unless an opt-out macro is defined.
  • Update the referenced Standard paper number in an existing diagnostic (N4988N5032).
  • Adjust the affected tests’ env.lst matrices to define the opt-out macro via PM_CL.
Show a summary per file
File Description
stl/inc/__msvc_string_view.hpp Adds the new char_traits restriction + updates diagnostic wording/reference.
tests/std/tests/P0980R1_constexpr_strings/env.lst Adds crosslist entry to define the escape-hatch macro for this test matrix.
tests/std/tests/Dev10_860410_bitset_ctors/env.lst Adds crosslist entry to define the escape-hatch macro for this test matrix.

Copilot's findings

Tip

Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

  • Files reviewed: 3/3 changed files
  • Comments generated: 4

Comment thread stl/inc/__msvc_string_view.hpp Outdated
#ifndef _ALLOW_ANY_TYPE_STD_CHAR_TRAIS
static_assert(is_integral_v<_Elem>,
"Standard char_traits is only provided for char, wchar_t, char8_t, char16_t, and char32_t. "
"See N5032 [char.traits]. Visual C++ accepts other unsigned integral types as an extension.");

Copilot AI Apr 29, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This static_assert checks is_integral_v<_Elem>, but the diagnostic text says Visual C++ accepts "other unsigned integral types". Either the check should match the wording (e.g., unsigned-only) or the wording should be updated to reflect what’s actually permitted (integral, including signed types like signed char).

Suggested change
"See N5032 [char.traits]. Visual C++ accepts other unsigned integral types as an extension.");
"See N5032 [char.traits]. Visual C++ accepts other integral types as an extension.");

Copilot uses AI. Check for mistakes.

@AlexGuteniev AlexGuteniev Apr 29, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. This is deliberate. I intentinally kept the documented limit the same as in find_meow_of, but had to allow signed types too to pass test.
I'll leave this to the maintainer whether we should apply the suggested change.

Comment thread tests/std/tests/Dev10_860410_bitset_ctors/env.lst Outdated
Comment thread tests/std/tests/P0980R1_constexpr_strings/env.lst Outdated
Copilot AI review requested due to automatic review settings May 30, 2026 16:44
@AraHaan

AraHaan commented Jul 14, 2026

Copy link
Copy Markdown

This PR would be source breaking because one can use this trick to allow the ifstream's read and ofstream's write functions to accept any character pointer of signed char, unsigned char, signed wchar_t, and unsigned wchar_t all with the same file handle because it would be changed from const _Elem* ptr to const void* ptr.

If this source breaking change must be done, consider adding an overload for read and write in iostreams as it do be sucking to be forced to use reinterpret_cast on the pointers. Also there might be use cases for being able to read and write both char and wchar_ts to and from the same file without opening it multiple times (as they could be locking the file exclusively when they open it the first time making subsequent open requests fail).

Also not only does reinterpret_cast<char*>() of a wchar_t* field when reading parts of a file to a struct feel ugly, but I fear that casting any wchar_t* pointer down to a char* can result in problems where it won't read the contents into said pointer correctly resulting in bugs in the user's code which makes it good to be able to pass in any pointer type to read/write and accept the consequences later 😂.

Now if _Char_traits, _WChar_traits, & _Narrow_char_traits could be merged and then have that allow merging all the char_traits specializations together I would be happy.

template <>
struct char_traits<char16_t> : _WChar_traits<char16_t> {};

template <>
struct char_traits<char32_t> : _Char_traits<char32_t, unsigned int> {};

template <>
struct char_traits<wchar_t> : _WChar_traits<wchar_t> {};

#ifdef _CRTBLD
template <>
struct char_traits<unsigned short> : _WChar_traits<unsigned short> {};
#endif // defined(_CRTBLD)

// snip.
template <>
struct char_traits<char> : _Narrow_char_traits<char, int> {}; // properties of a string or stream char element

#ifdef __cpp_char8_t
template <>
struct char_traits<char8_t> : _Narrow_char_traits<char8_t, unsigned int> {};
#endif // defined(__cpp_char8_t)

(there may be more, these I found in __msvc_string_view.hpp).

Edit: the above std::basic_ifstream and std::basic_ofstream's with void passed in them will not compile anyways.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement Something can be improved

Projects

Status: Initial Review

Development

Successfully merging this pull request may close these issues.

4 participants