Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

<exception>: exception's constructor from const char* is non-conforming #882

Open
alexismailov2 opened this issue Jun 6, 2020 · 16 comments
Labels
bug Something isn't working

Comments

@alexismailov2
Copy link

alexismailov2 commented Jun 6, 2020

explicit __CLR_OR_THIS_CALL exception(const char* _Message = "unknown", int = 1) noexcept : _Ptr(_Message) {}

@miscco
Copy link
Contributor

miscco commented Jun 6, 2020

Could you please edit the issue so that the content is in the description and the title is short and precise?

@alexismailov2 alexismailov2 changed the title exception class should not have constructor with string it brokes compatibility with gcc stl and clang stl... Also standart describe that exception should be just base class without any strings or pointers to characters internally... Exception class should not have a constructor with string... Jun 6, 2020
@alexismailov2
Copy link
Author

exception class should not have constructor with string it brokes compatibility with gcc stl and clang stl... Also standart describe that exception should be just base class without any strings or pointers to characters internally...

@CaseyCarter CaseyCarter added the bug Something isn't working label Jun 6, 2020
@CaseyCarter
Copy link
Contributor

exception class should not have constructor with string it brokes compatibility with gcc stl and clang stl...

Can you give an example of a program that would be well-formed in the absence of this constructor that is rendered ill-formed by its presence?

Also standart describe that exception should be just base class without any strings or pointers to characters internally...

The standard does not specify private class members. It does sometimes depict private class members for purposes of exposition, but a standard library class isn't required to have private members depicted in the standard and may have other private members not depicted in the standard.

@CaseyCarter CaseyCarter added the info needed We need more info before working on this label Jun 6, 2020
@timsong-cpp
Copy link
Contributor

Direct-initialization from a type with user-defined conversions to both exception and const char*? Both GCC and clang would reject such a case if there's a constructor from const char*; MSVC doesn't but that seems nonconforming (the ICSes are indistinguishable).

@CaseyCarter CaseyCarter changed the title Exception class should not have a constructor with string... <exception>: exception's constructor from const char* is non-conforming Jun 13, 2020
@CaseyCarter CaseyCarter removed the info needed We need more info before working on this label Jun 13, 2020
@Berrysoft
Copy link
Contributor

I think we all misread the code. This exception class is in namespace stdext, and the real std::exception, the one is conforming, is in vcruntime_exception.h, which is not in this repo.

@CaseyCarter
Copy link
Contributor

I think we all misread the code. This exception class is in namespace stdext, and the real std::exception, the one is conforming, is in vcruntime_exception.h, which is not in this repo.

std::exception is defined in <vcruntime_exception.h> when _HAS_EXCEPTIONS == 1, it has similarly problematic constructor overload(s). stdext::exception is pulled into std via using-declaration when _HAS_EXCEPTIONS == 0. Ideally we'll fix both classes.

Unfortunately we haven't managed to open-source vcruntime yet, so at least that part will have to be done by a MSFT employee. The fact that it's weirdness in vcruntime means we'll also need to determine if the compiler depends on that weirdness or not, and possibly spread the fixing even further.

@Berrysoft
Copy link
Contributor

Berrysoft commented Jun 19, 2020

Ok, I didn't notice _HAS_EXCPTIONS, but it is also an extension, which is not conforming, right?

Actually I don't think it is problematic. The standard library should allow all conforming code, but needn't prevent nonconforming code. There are many extensions in this STL, and all could be stated as nonconforming - some methods is marked noexcept but the standard not (too many), some methods has other default parameters but the standard not (for example, std::basic_fstream::open(const char*, std::ios_base::openmode, int)), some constructors is not in the standard (for example, std::basic_fstream::basic_fstream(FILE*)), and even some overloads is provided when wchar_t is defined as a native type instead of a typedef of unsigned short. If you write portable code, you simply do not rely on these extensions.

I agree that if you write some code like

template <typename T, typename = void>
struct has_pointer_to_const_char_constructor : std::false_type {}

template <typename T>
struct has_pointer_to_const_char_constructor<T, std::enable_if_t<decltype(T{std::declval<const char*>()})>> : std::true_type {}

static_assert(!has_pointer_to_const_char_constructor<std::exception>::value);

you will get unexcepted result, but I cannot understand why you would do that.

@timsong-cpp
Copy link
Contributor

timsong-cpp commented Jun 19, 2020

The whole point of Casey's original question (and my comment in response) is that this extension in fact breaks some (arguably contrived) conforming code.

@Berrysoft
Copy link
Contributor

Berrysoft commented Jun 19, 2020

Seems weird, but OK.

But there are many other classes which has constructors not conforming. Do you mean that they should all be modified?

@StephanTLavavej
Copy link
Member

The Standard explicitly permits noexcept strengthening, and it also permits constructors and other non-virtual member functions to vary with default arguments. However, it requires implementations to otherwise behave as if no additional overloads are declared. exception(const char*) and basic_fstream(FILE*) violate that rule, but I'd say that the former is significantly less desirable than the latter.

The problem with removing this, aside from the std vs. stdext issue, is that there is a ridiculous amount of user code that has grown to be dependent on this bug/extension. I think we'll need to Microsoft-deprecate it for a number of years before we can contemplate outright removal (and I am a huge proponent of removing non-Standard extensions).

@achabense
Copy link
Contributor

exception(const char*) and basic_fstream(FILE*) are still not deprecated; is it time to deprecate them now?

@alexismailov2
Copy link
Author

OMG Microsoft still not fixed it? Really? It was created 3 years ago. Thanks all for help them completely to fix that and be close to universal solution which will work the same on different compilers!

@achabense
Copy link
Contributor

constexpr initializer_list(const _Elem* _First_arg, const _Elem* _Last_arg) noexcept
: _First(_First_arg), _Last(_Last_arg) {}

This looks non-conforming too.

@frederick-vs-ja
Copy link
Contributor

constexpr initializer_list(const _Elem* _First_arg, const _Elem* _Last_arg) noexcept
: _First(_First_arg), _Last(_Last_arg) {}

This looks non-conforming too.

I think MSVC needs to be fixed.

When a backing array (shown as __arr for the purpose of exposition, with its length denoted by __size) is created in list-initialization, MSVC currently needs to direct-initialize the initializer_list object with (__arr, __arr + __size) (Example 1). But MSVC doesn't make any special access currently, so the corresponding constructor needs to be public (Example 2).

@alexismailov2
Copy link
Author

@fredefick-vs-ja I think Microsoft does not completely understand still the problem. Big software organisation in this world not equal to expert in C++. It is completely different things. I really very upset than Microsoft given 100500 their certified employees and nobody still can answer something close to technical understanding this problem!!!

@StephanTLavavej
Copy link
Member

I understand this problem, and I could fix it today if I wanted to. But it would break an absolutely ridiculous amount of non-conforming user code, and many users don't immediately realize what's happening (i.e. "oh, my code is bad, I need to fix it") when they encounter such breaks. Sometimes third-party libraries are involved which is legitimately logistically challenging. This interferes with timely upgrading, which is counterproductive for moving the C++ ecosystem forward.

MSVC's STL is actually fairly aggressive (for a C++ implementation in general, and Microsoft in particular) about making changes to improve conformance at the cost of breaking user builds. We routinely deprecate and remove (with escape hatches, usually) things all the time. But in this specific case, we currently believe that the benefits don't outweigh the considerable costs, both to users (who have to deal with the breaks) and to maintainers (who have to fix a significant amount of broken code ourselves - both in the open-source "Real World Code" projects we build in our test suite, and in the massive amount of Microsoft-internal code that we're indirectly responsible for). In particular, the maintainer team is both very busy (reviewing feature and bugfix work) and very time-constrained for the foreseeable future (I am currently the only FTE able to dedicate ~100% of my time to the STL), so we have to ruthlessly prioritize our work, and this is not a priority. Sorry.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

8 participants