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

<expected> in permissive mode: error C7608: atomic constraint should be a constant expression #4657

Closed
danielvandenberg95 opened this issue May 5, 2024 · 4 comments · Fixed by #4658
Labels
bug Something isn't working fixed Something works now, yay!

Comments

@danielvandenberg95
Copy link

danielvandenberg95 commented May 5, 2024

Describe the bug

In one of my projects, std::expected throws errors
error C7608: atomic constraint should be a constant expression
and
error C2131: expression did not evaluate to a constant
I think the latter is caused by the former.

In order to reproduce the issue, I inlined headers, removing them untill the issue disappeared. and discarding as much code as possible. I ended up with the following code in one file in my project:

// xloctime internal header

// Copyright (c) Microsoft Corporation.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

#ifndef _XLOCTIME_
#define _XLOCTIME_
#include <yvals_core.h>
#if _STL_COMPILER_PREPROCESSOR
#include <ctime>
#include <iterator>
#include <xlocnum>

#pragma pack(push, _CRT_PACKING)
#pragma warning(push, _STL_WARNING_LEVEL)
#pragma warning(disable : _STL_DISABLED_WARNINGS)
_STL_DISABLE_CLANG_WARNINGS
#pragma push_macro("new")
#undef new

_STD_BEGIN

_EXPORT_STD extern "C++" template <class _Elem, class _OutIt = ostreambuf_iterator<_Elem, char_traits<_Elem>>>
class time_put : public locale::facet { // facet for converting encoded times to text
public:
protected:
    __CLR_OR_THIS_CALL ~time_put() noexcept override {}

    virtual _OutIt __CLR_OR_THIS_CALL do_put(_OutIt _Dest, ios_base& _Iosbase, _Elem, const tm* _Pt, char _Specifier,
        char _Modifier = '\0') const {
        size_t _Count = 0;
        string _Str = "0";
        return _STD copy(&_Str[1], &_Str[_Count], _Dest); // Removing this line (replacing with return nullptr) causes the error to disappear.
    }

private:
    _Locinfo::_Timevec _Tnames; // locale-specific stuff for _Strftime
};


#if defined(_DLL_CPPLIB)

#if !defined(_CRTBLD) || defined(__FORCE_INSTANCE)
template class _CRTIMP2_PURE_IMPORT time_put<char, ostreambuf_iterator<char, char_traits<char>>>;
#endif // !defined(_CRTBLD) || defined(__FORCE_INSTANCE)

#endif // defined(_DLL_CPPLIB)
_STD_END

#pragma pop_macro("new")
_STL_RESTORE_CLANG_WARNINGS
#pragma warning(pop)
#pragma pack(pop)
#endif // _STL_COMPILER_PREPROCESSOR
#endif // _XLOCTIME_


#include <expected>
#include <string>

struct test {
    int x;
    int y;
};
using err = std::string;

std::expected<std::pair<int, int>, err> testFunction() {
    return std::pair<int, int>{ 5, 5 };
}

The full output I get while compiling this is:

Build started at 16:36...
1>------ Build started: Project: MyProject, Configuration: Debug x64 ------
1>test.cpp
1>C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.39.33519\include\expected(572,18): error C7608: atomic constraint should be a constant expression
1>(compiling source file 'test.cpp')
1>C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.39.33519\include\expected(572,18):
1>the template instantiation context (the oldest one first) is
1>	V:\Projects\MyProject\MyProject\test.cpp(77,97):
1>	see reference to class template instantiation 'std::time_put<char,std::ostreambuf_iterator<char,std::char_traits<char>>>' being compiled
1>	V:\Projects\MyProject\MyProject\test.cpp(35,39):
1>	while compiling class template member function '_OutIt std::time_put<char,_OutIt>::do_put(_OutIt,std::ios_base &,_Elem,const tm *,char,char) const'
1>        with
1>        [
1>            _OutIt=std::ostreambuf_iterator<char,std::char_traits<char>>,
1>            _Elem=char
1>        ]
1>	V:\Projects\MyProject\MyProject\test.cpp(66,21):
1>	see reference to function template instantiation '_OutIt std::copy<char*,_OutIt>(_InIt,_InIt,_OutIt)' being compiled
1>        with
1>        [
1>            _OutIt=std::ostreambuf_iterator<char,std::char_traits<char>>,
1>            _InIt=char *
1>        ]
1>	C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.39.33519\include\xutility(4589,31):
1>	see reference to function template instantiation '_OutIt std::_Copy_unchecked<_InIt,_InIt,_OutIt>(_InIt,_Sent,_OutIt)' being compiled
1>        with
1>        [
1>            _OutIt=std::ostreambuf_iterator<char,std::char_traits<char>>,
1>            _InIt=char *,
1>            _Sent=char *
1>        ]
1>	C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.39.33519\include\xutility(4559,23):
1>	see reference to alias template instantiation 'std::_Sent_copy_cat<_InIt,_Sent,_OutIt>' being compiled
1>        with
1>        [
1>            _InIt=char *,
1>            _Sent=char *,
1>            _OutIt=std::ostreambuf_iterator<char,std::char_traits<char>>
1>        ]
1>	C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.39.33519\include\xutility(4446,28):
1>	see reference to variable template 'const bool _Iterators_are_contiguous<char *,std::ostreambuf_iterator<char,std::char_traits<char> > >' being compiled
1>	C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.39.33519\include\xutility(4383,40):
1>	see reference to variable template 'const bool _Iterator_is_contiguous<std::ostreambuf_iterator<char,std::char_traits<char> > >' being compiled
1>	C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.39.33519\include\xutility(4355,49):
1>	see reference to variable template 'bool contiguous_iterator<std::ostreambuf_iterator<char,std::char_traits<char> > >' being compiled
1>	C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.39.33519\include\xutility(819,31):
1>	see reference to variable template 'bool random_access_iterator<std::ostreambuf_iterator<char,std::char_traits<char> > >' being compiled
1>	C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.39.33519\include\xutility(807,34):
1>	see reference to variable template 'bool bidirectional_iterator<std::ostreambuf_iterator<char,std::char_traits<char> > >' being compiled
1>	C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.39.33519\include\xutility(800,34):
1>	see reference to variable template 'bool forward_iterator<std::ostreambuf_iterator<char,std::char_traits<char> > >' being compiled
1>	C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.39.33519\include\xutility(796,28):
1>	see reference to variable template 'bool input_iterator<std::ostreambuf_iterator<char,std::char_traits<char> > >' being compiled
1>	C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.39.33519\include\xutility(785,26):
1>	see reference to variable template 'bool input_or_output_iterator<std::ostreambuf_iterator<char,std::char_traits<char> > >' being compiled
1>	C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.39.33519\include\__msvc_iter_core.hpp(416,8):
1>	see reference to variable template 'bool weakly_incrementable<std::ostreambuf_iterator<char,std::char_traits<char> > >' being compiled
1>	C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.39.33519\include\__msvc_iter_core.hpp(402,32):
1>	see reference to variable template 'bool movable<std::ostreambuf_iterator<char,std::char_traits<char> > >' being compiled
1>	C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.39.33519\include\concepts(245,8):
1>	see reference to variable template 'bool swappable<std::ostreambuf_iterator<char,std::char_traits<char> > >' being compiled
1>	C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.39.33519\include\concepts(124,26):
1>	see reference to variable template 'bool _Use_ADL_swap<std::ostreambuf_iterator<char,std::char_traits<char> > &,std::ostreambuf_iterator<char,std::char_traits<char> > &>' being compiled
1>	C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.39.33519\include\expected(572,18):
1>	see reference to variable template 'const bool is_swappable_v<std::pair<int,int> >' being compiled
1>	C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.39.33519\include\type_traits(2186,60):
1>	see reference to class template instantiation 'std::_Is_swappable<_Ty>' being compiled
1>        with
1>        [
1>            _Ty=std::pair<int,int>
1>        ]
1>	C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.39.33519\include\type_traits(2147,102):
1>	see reference to class template instantiation 'std::_Is_swappable_with<_Ty &,_Ty &>' being compiled
1>        with
1>        [
1>            _Ty=std::pair<int,int>
1>        ]
1>	C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.39.33519\include\type_traits(2142,21):
1>	see reference to variable template 'const bool conjunction_v<std::_Swappable_with_helper<std::pair<int,int> &,std::pair<int,int> &,void>,std::_Swappable_with_helper<std::pair<int,int> &,std::pair<int,int> &,void> >' being compiled
1>C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.39.33519\include\expected(572,18): error C2131: expression did not evaluate to a constant
1>(compiling source file 'test.cpp')
1>C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.39.33519\include\expected(572,18):
1>failure was caused by a read of an uninitialized symbol
1>C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.39.33519\include\expected(572,18):
1>see usage of 'std::is_swappable_v<std::pair<int,int>>'
1>Done building project "MyProject.vcxproj" -- FAILED.
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========
========== Build completed at 16:36 and took 01.019 seconds ==========

When enabling a precompiled header, this error goes away. I can not enable a precompiled header for this project though.

Command-line test case

I have not been able to reproduce it in command line. My attempt has been with:
cl /permissive /MP /GS /TP /W4 /wd"4458" /Gy- /Zc:wchar_t /Zi /Gm- /Od /Ob0 /sdl /Zc:inline /fp:precise /D "_SILENCE_ALL_CXX20_DEPRECATION_WARNINGS" /D "_MBCS" /D "_SILENCE_CXX23_ALIGNED_STORAGE_DEPRECATION_WARNING" /D "_HEAPDEBUG=0" /D "DEBUG=1" /fp:except- /errorReport:prompt /WX- /Zc:forScope /RTC1 /Gd /MDd /std:c++latest /FC /EHsc /nologo /diagnostics:column /JMC .\repro.cpp
as these are the closest to the command line options that my actual project is using.

Expected behavior

I would expect this to compile without issue.

STL version

Microsoft Visual Studio Professional 2022
Version 17.9.6

Additional context

I'm still trying to narrow down the issue, but seem to be stuck.

@danielvandenberg95
Copy link
Author

danielvandenberg95 commented May 5, 2024

Got it reproduced:

CL.exe /c /Zi /JMC /nologo /W4 /WX- /diagnostics:column /sdl /MP24 /Od /Ob0 /D _SILENCE_ALL_CXX20_DEPRECATION_WARNINGS /D _SILENCE_CXX23_ALIGNED_STORAGE_DEPRECATION_WARNING /D _HEAPDEBUG=0 /D DEBUG=1 /D _MBCS /Gm- /EHsc /RTC1 /MDd /GS /Gy- /fp:precise /fp:except- /Zc:wchar_t /Zc:forScope /Zc:inline /std:c++latest /permissive /external:W4 /Gd /TP /wd4458 /FC /errorReport:prompt /MP /Zc:__cplusplus repro.cpp

#include <yvals_core.h>
#include <xloctime>

#include <expected>
#include <string>

struct test {
    int x;
    int y;
};
using err = std::string;

std::expected<std::pair<int, int>, err> testFunction() {
    return std::pair<int, int>{ 5, 5 };
}

The problem disappears when removing /permissive

@frederick-vs-ja
Copy link
Contributor

frederick-vs-ja commented May 6, 2024

Confirmed that /permissive /MD and /permissive /MDd can cause the problem. /permissive- (by default in C++20 and later modes), /MT, and /MTd are fine. Godbolt link.

Furtherly reduced example:

#include <xloctime>
//
#include <expected>
#include <string>

std::expected<std::pair<int, int>, std::string> testFunction() {
    return std::pair<int, int>{ 5, 5 };
}

At the first glance, I guess this is a compiler bug. But also note that <xloctime> is an internal header which shouldn't be directly included by user code.

Can you reproduce the problem with only standard headers (directly) included?

Edit:
This example only includes necessary standard headers and reproduces the problem (Godbolt link).

#include <expected> // for std::expected
#include <locale>   // explicit instantiation definition(s) in <locale> can be problematic
#include <string>   // for std::string
#include <utility>  // for std::pair

std::expected<std::pair<int, int>, std::string> testFunction() {
    return std::pair<int, int>{5, 5};
}

@cpplearner
Copy link
Contributor

Of course they can. Just replace <xloctime> with <locale>.

@frederick-vs-ja
Copy link
Contributor

frederick-vs-ja commented May 6, 2024

Roughly reduced to the following (Godbolt link):

#include <concepts>  // for std::ranges::swap and std::same_as
#include <expected>  // for std::expected
#include <string>    // for std::string and std::char_traits
#include <utility>   // for std::declval

template <class ElemT, class Traits>
struct my_outputter {};

template <class ElemT, class Outputter = ::my_outputter<ElemT, std::char_traits<ElemT>>>
struct my_putter {
    void do_put_like() const {
        // /permissive causes the ADL-found std::swap to be selected for my_outputter<char, std::char_traits<char>>
        static_assert(std::same_as<
            decltype(std::ranges::swap(std::declval<Outputter&>(), std::declval<Outputter&>())), void>);
    }
};

// explicit instantiation can be problematic
template struct my_putter<char, ::my_outputter<char, std::char_traits<char>>>; 

std::expected<int, std::string> testFunction() {
    return 42;
}

/MD and /MDd merely trigger the explicit instantiation via conditional compilation, so only the /permissive option is actually problematic IIUC.

In MSVC STL, std::copy needs to detect whether the iterator is contiguous, via std::contiguous_iterator since C++20. and std::contiguous_iterator eventually relies on std::swappable which uses std::ranges::swap. It seems that ADL in std::ranges::swap behaves differently in /permissive and /permissive- modes. #4363 may be related.

This is probably a bug of MSVC (or the nature of permissive modes). Workaround seems possible (Godbolt link).

Edit: Reported DevCom-10652420. The major reason of the bug doesn't seem to be the behavioral difference of ADL.

@danielvandenberg95 danielvandenberg95 changed the title <expected> + <xloctime>: error C7608: atomic constraint should be a constant expression <expected> in permissive mode: error C7608: atomic constraint should be a constant expression May 6, 2024
@StephanTLavavej StephanTLavavej added the bug Something isn't working label May 8, 2024
@StephanTLavavej StephanTLavavej added the fixed Something works now, yay! label May 20, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working fixed Something works now, yay!
Projects
None yet
4 participants