diff --git a/.clang-format b/.clang-format index 23c5c81a88..3871a6748a 100644 --- a/.clang-format +++ b/.clang-format @@ -265,6 +265,8 @@ SpaceBeforeParensOptions: # NOTE: _STD_BEGIN, _STD_END, etc. aren't macros for complete statements, but telling # clang-format that they are produces the behavior that we want (with no block indentation). StatementMacros: + - _EXTERN_CXX_WORKAROUND + - _END_EXTERN_CXX_WORKAROUND - _STD_BEGIN - _STD_END - _STDEXT_BEGIN diff --git a/stl/inc/cmath b/stl/inc/cmath index 8e3ac832bd..8bb994af15 100644 --- a/stl/inc/cmath +++ b/stl/inc/cmath @@ -33,6 +33,7 @@ _STL_DISABLE_CLANG_WARNINGS #pragma push_macro("new") #undef new +_EXTERN_CXX_WORKAROUND _NODISCARD _Check_return_ inline float acos(_In_ float _Xx) noexcept /* strengthened */ { return _CSTD acosf(_Xx); } @@ -552,6 +553,7 @@ _NODISCARD _Check_return_ inline long double trunc(_In_ long double _Xx) noexcep return _CSTD truncl(_Xx); #endif // ^^^ intrinsics unavailable ^^^ } +_END_EXTERN_CXX_WORKAROUND _STD_BEGIN template @@ -560,6 +562,7 @@ using _Common_float_type_t = conditional_t || is_sa double>>; // find type for two-argument math function _STD_END +_EXTERN_CXX_WORKAROUND template , int> = 0> double frexp(_Ty _Value, _Out_ int* const _Exp) noexcept /* strengthened */ { return _CSTD frexp(static_cast(_Value), _Exp); @@ -709,6 +712,7 @@ _GENERIC_MATH2(fmin) #undef _GENERIC_MATH2 #undef _GENERIC_MATH2I #undef _HAS_CMATH_INTRINSICS +_END_EXTERN_CXX_WORKAROUND _STD_BEGIN _EXPORT_STD using _CSTD abs; diff --git a/stl/inc/cstddef b/stl/inc/cstddef index 857e8a30c6..fb5fc321a1 100644 --- a/stl/inc/cstddef +++ b/stl/inc/cstddef @@ -98,7 +98,9 @@ _NODISCARD _MSVC_INTRINSIC constexpr _IntType to_integer(const byte _Arg) noexce _STD_END +_EXTERN_CXX_WORKAROUND using _STD max_align_t; // intentional, for historical reasons +_END_EXTERN_CXX_WORKAROUND // TRANSITION, non-_Ugly attribute tokens #pragma pop_macro("intrinsic") diff --git a/stl/inc/cstdlib b/stl/inc/cstdlib index 30a17994d5..1bf0509fe3 100644 --- a/stl/inc/cstdlib +++ b/stl/inc/cstdlib @@ -18,6 +18,7 @@ _STL_DISABLE_CLANG_WARNINGS #pragma push_macro("new") #undef new +_EXTERN_CXX_WORKAROUND // has abs(long) and abs(long long) _NODISCARD _Check_return_ inline double abs(_In_ double _Xx) noexcept /* strengthened */ { return _CSTD fabs(_Xx); @@ -30,6 +31,7 @@ _NODISCARD _Check_return_ inline float abs(_In_ float _Xx) noexcept /* strengthe _NODISCARD _Check_return_ inline long double abs(_In_ long double _Xx) noexcept /* strengthened */ { return _CSTD fabsl(_Xx); } +_END_EXTERN_CXX_WORKAROUND _STD_BEGIN _EXPORT_STD using _CSTD size_t; diff --git a/stl/inc/ctime b/stl/inc/ctime index 905b090e47..ae2f152edc 100644 --- a/stl/inc/ctime +++ b/stl/inc/ctime @@ -29,44 +29,44 @@ _EXPORT_STD using _CSTD strftime; _EXPORT_STD using _CSTD timespec; #endif // _HAS_CXX17 -#ifdef _BUILD_STD_MODULE // TRANSITION, OS-33790456 +#ifdef _BUILD_STD_MODULE // TRANSITION, OS-33790456; `template ` avoids ambiguity _STL_DISABLE_DEPRECATED_WARNING -_EXPORT_STD +_EXPORT_STD template _Check_return_ _CRT_INSECURE_DEPRECATE(ctime_s) inline char* __CRTDECL ctime(_In_ const time_t* const _Time) noexcept /* strengthened */ { return _CSTD _ctime64(_Time); } -_EXPORT_STD +_EXPORT_STD template _Check_return_ inline double __CRTDECL difftime(_In_ const time_t _Time1, _In_ const time_t _Time2) noexcept /* strengthened */ { return _CSTD _difftime64(_Time1, _Time2); } -_EXPORT_STD +_EXPORT_STD template _Check_return_ _CRT_INSECURE_DEPRECATE(gmtime_s) inline tm* __CRTDECL gmtime(_In_ const time_t* const _Time) noexcept /* strengthened */ { return _CSTD _gmtime64(_Time); } -_EXPORT_STD +_EXPORT_STD template _CRT_INSECURE_DEPRECATE(localtime_s) inline tm* __CRTDECL localtime(_In_ const time_t* const _Time) noexcept /* strengthened */ { return _CSTD _localtime64(_Time); } -_EXPORT_STD +_EXPORT_STD template _Check_return_opt_ inline time_t __CRTDECL mktime(_Inout_ tm* const _Tm) noexcept /* strengthened */ { return _CSTD _mktime64(_Tm); } -_EXPORT_STD +_EXPORT_STD template inline time_t __CRTDECL time(_Out_opt_ time_t* const _Time) noexcept /* strengthened */ { return _CSTD _time64(_Time); } -_EXPORT_STD +_EXPORT_STD template _Check_return_ inline int __CRTDECL timespec_get(_Out_ timespec* const _Ts, _In_ const int _Base) noexcept /* strengthened */ { return _CSTD _timespec64_get(reinterpret_cast<_timespec64*>(_Ts), _Base); diff --git a/stl/inc/cwchar b/stl/inc/cwchar index 6922507bbb..5b2574db37 100644 --- a/stl/inc/cwchar +++ b/stl/inc/cwchar @@ -18,7 +18,9 @@ _STL_DISABLE_CLANG_WARNINGS #pragma push_macro("new") #undef new +_EXTERN_CXX_WORKAROUND using _Mbstatet = mbstate_t; +_END_EXTERN_CXX_WORKAROUND _STD_BEGIN #pragma warning(push) diff --git a/stl/inc/xfilesystem_abi.h b/stl/inc/xfilesystem_abi.h index fb323a818d..79ad2b6685 100644 --- a/stl/inc/xfilesystem_abi.h +++ b/stl/inc/xfilesystem_abi.h @@ -78,7 +78,9 @@ enum class __std_fs_file_attr : unsigned long { }; } // extern "C" +_EXTERN_CXX_WORKAROUND _BITMASK_OPS(_EMPTY_ARGUMENT, __std_fs_file_attr) +_END_EXTERN_CXX_WORKAROUND extern "C" { enum class __std_fs_reparse_tag : unsigned long { @@ -123,7 +125,9 @@ enum class __std_fs_stats_flags : unsigned long { }; } // extern "C" +_EXTERN_CXX_WORKAROUND _BITMASK_OPS(_EMPTY_ARGUMENT, __std_fs_stats_flags) +_END_EXTERN_CXX_WORKAROUND extern "C" { struct __std_fs_stats { @@ -199,7 +203,9 @@ enum class __std_access_rights : unsigned long { }; } // extern "C" +_EXTERN_CXX_WORKAROUND _BITMASK_OPS(_EMPTY_ARGUMENT, __std_access_rights) +_END_EXTERN_CXX_WORKAROUND extern "C" { enum class __std_fs_file_flags : unsigned long { @@ -209,7 +215,9 @@ enum class __std_fs_file_flags : unsigned long { }; } // extern "C" +_EXTERN_CXX_WORKAROUND _BITMASK_OPS(_EMPTY_ARGUMENT, __std_fs_file_flags) +_END_EXTERN_CXX_WORKAROUND extern "C" { enum class __std_fs_file_handle : intptr_t { _Invalid = -1 }; @@ -236,7 +244,9 @@ enum class __std_fs_copy_options { }; } // extern "C" +_EXTERN_CXX_WORKAROUND _BITMASK_OPS(_EMPTY_ARGUMENT, __std_fs_copy_options) +_END_EXTERN_CXX_WORKAROUND extern "C" { _NODISCARD __std_ulong_and_error __stdcall __std_fs_get_full_path_name(_In_z_ const wchar_t* _Source, diff --git a/stl/inc/xutility b/stl/inc/xutility index 7f498383f4..260d8cf34c 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -4518,6 +4518,18 @@ _OutCtgIt _Copy_memmove_n(_CtgIt _First, const size_t _Count, _OutCtgIt _Dest) { template _INLINE_VAR constexpr bool _Is_vb_iterator = false; +template +_CONSTEXPR20 _OutIt _Copy_vbool(_VbIt _First, _VbIt _Last, _OutIt _Dest); + +template +_NODISCARD _CONSTEXPR20 _Iter_diff_t<_VbIt> _Count_vbool(_VbIt _First, _VbIt _Last, bool _Val) noexcept; + +template +_CONSTEXPR20 void _Fill_vbool(_VbIt _First, _VbIt _Last, bool _Val) noexcept; + +template +_NODISCARD _CONSTEXPR20 _VbIt _Find_vbool(_VbIt _First, _VbIt _Last, bool _Val) noexcept; + template _CONSTEXPR20 _OutIt _Copy_n_unchecked4(_InIt _First, _SizeTy _Count, _OutIt _Dest) { // copy _First + [0, _Count) to _Dest + [0, _Count), returning _Dest + _Count @@ -4556,7 +4568,7 @@ _CONSTEXPR20 _OutIt _Copy_unchecked(_InIt _First, _Sent _Last, _OutIt _Dest) { // copy [_First, _Last) to [_Dest, ...) // note: _Copy_unchecked has callers other than the copy family if constexpr (_Is_vb_iterator<_InIt> && _Is_vb_iterator<_OutIt, true>) { - return _Copy_vbool(_First, _Last, _Dest); + return _STD _Copy_vbool(_First, _Last, _Dest); } else { if constexpr (_Sent_copy_cat<_InIt, _Sent, _OutIt>::_Bitcopy_assignable) { #if _HAS_CXX20 @@ -4753,7 +4765,7 @@ _CONSTEXPR20 _OutIt copy_n(_InIt _First, _Diff _Count_raw, _OutIt _Dest) { _Algorithm_int_t<_Diff> _Count = _Count_raw; if (0 < _Count) { if constexpr (_Is_vb_iterator<_InIt> && _Is_vb_iterator<_OutIt, true>) { - return _Copy_vbool(_First, _First + _Count, _Dest); + return _STD _Copy_vbool(_First, _First + _Count, _Dest); } else { auto _UFirst = _Get_unwrapped_n(_First, _Count); auto _UDest = _Get_unwrapped_n(_Dest, _Count); @@ -4856,7 +4868,7 @@ _CONSTEXPR20 _OutIt _Move_unchecked(_InIt _First, _InIt _Last, _OutIt _Dest) { // move [_First, _Last) to [_Dest, ...) // note: _Move_unchecked has callers other than the move family if constexpr (_Is_vb_iterator<_InIt> && _Is_vb_iterator<_OutIt, true>) { - return _Copy_vbool(_First, _Last, _Dest); + return _STD _Copy_vbool(_First, _Last, _Dest); } else { if constexpr (_Iter_move_cat<_InIt, _OutIt>::_Bitcopy_assignable) { #if _HAS_CXX20 @@ -5006,7 +5018,7 @@ _CONSTEXPR20 void fill(const _FwdIt _First, const _FwdIt _Last, const _Ty& _Val) // copy _Val through [_First, _Last) _Adl_verify_range(_First, _Last); if constexpr (_Is_vb_iterator<_FwdIt, true>) { - _Fill_vbool(_First, _Last, _Val); + _STD _Fill_vbool(_First, _Last, _Val); } else { auto _UFirst = _Get_unwrapped(_First); const auto _ULast = _Get_unwrapped(_Last); @@ -5048,7 +5060,7 @@ _CONSTEXPR20 _OutIt fill_n(_OutIt _Dest, const _Diff _Count_raw, const _Ty& _Val if (0 < _Count) { if constexpr (_Is_vb_iterator<_OutIt, true>) { const auto _Last = _Dest + static_cast(_Count); - _Fill_vbool(_Dest, _Last, _Val); + _STD _Fill_vbool(_Dest, _Last, _Val); return _Last; } else { auto _UDest = _Get_unwrapped_n(_Dest, _Count); @@ -5814,7 +5826,7 @@ _EXPORT_STD template _NODISCARD _CONSTEXPR20 _InIt find(_InIt _First, const _InIt _Last, const _Ty& _Val) { // find first matching _Val _Adl_verify_range(_First, _Last); if constexpr (_Is_vb_iterator<_InIt> && is_same_v<_Ty, bool>) { - return _Find_vbool(_First, _Last, _Val); + return _STD _Find_vbool(_First, _Last, _Val); } else { _Seek_wrapped(_First, _STD _Find_unchecked(_Get_unwrapped(_First), _Get_unwrapped(_Last), _Val)); return _First; @@ -5944,7 +5956,7 @@ _NODISCARD _CONSTEXPR20 _Iter_diff_t<_InIt> count(const _InIt _First, const _InI // count elements that match _Val _Adl_verify_range(_First, _Last); if constexpr (_Is_vb_iterator<_InIt> && is_same_v<_Ty, bool>) { - return _Count_vbool(_First, _Last, _Val); + return _STD _Count_vbool(_First, _Last, _Val); } else { auto _UFirst = _Get_unwrapped(_First); const auto _ULast = _Get_unwrapped(_Last); diff --git a/stl/inc/yvals_core.h b/stl/inc/yvals_core.h index cb6e0251fd..1c494ad110 100644 --- a/stl/inc/yvals_core.h +++ b/stl/inc/yvals_core.h @@ -1941,16 +1941,49 @@ compiler option, or define _ALLOW_RTCc_IN_STL to suppress this error. #define _STRINGIZE(x) _STRINGIZEX(x) #define _EMPTY_ARGUMENT // for empty macro argument -#define _STD_BEGIN namespace std { -#define _STD_END } -#define _STD ::std:: -#define _CHRONO ::std::chrono:: -#define _RANGES ::std::ranges:: +// extern "C++" attaches declarations to the global module, see N4964 [module.unit]/7.2. +// It has no effect in C++14/17. + +// In the STL's headers (which might be used to build the named module std), we unconditionally +// and directly mark declarations of our separately compiled machinery as extern "C++", allowing +// the named module to work with the separately compiled code (which is always built classically). + +// TRANSITION: _USE_EXTERN_CXX_EVERYWHERE_FOR_STL controls whether we also wrap the STL's +// header-only code in this linkage-specification, as a temporary workaround to allow +// the named module to coexist with classic includes in the same translation unit. + +#ifndef _USE_EXTERN_CXX_EVERYWHERE_FOR_STL +#define _USE_EXTERN_CXX_EVERYWHERE_FOR_STL _HAS_CXX20 +#endif // ^^^ !defined(_USE_EXTERN_CXX_EVERYWHERE_FOR_STL) ^^^ + +#if _USE_EXTERN_CXX_EVERYWHERE_FOR_STL +#define _EXTERN_CXX_WORKAROUND extern "C++" { +#define _END_EXTERN_CXX_WORKAROUND } +#else // ^^^ _USE_EXTERN_CXX_EVERYWHERE_FOR_STL / !_USE_EXTERN_CXX_EVERYWHERE_FOR_STL vvv +#define _EXTERN_CXX_WORKAROUND +#define _END_EXTERN_CXX_WORKAROUND +#endif // ^^^ !_USE_EXTERN_CXX_EVERYWHERE_FOR_STL ^^^ + +#define _STD_BEGIN \ + _EXTERN_CXX_WORKAROUND \ + namespace std { +#define _STD_END \ + } \ + _END_EXTERN_CXX_WORKAROUND + +#define _STD ::std:: +#define _CHRONO ::std::chrono:: +#define _RANGES ::std::ranges:: // We use the stdext (standard extension) namespace to contain extensions that are not part of the current standard -#define _STDEXT_BEGIN namespace stdext { -#define _STDEXT_END } -#define _STDEXT ::stdext:: +#define _STDEXT_BEGIN \ + _EXTERN_CXX_WORKAROUND \ + namespace stdext { +#define _STDEXT_END \ + } \ + _END_EXTERN_CXX_WORKAROUND + +#define _STDEXT ::stdext:: #define _CSTD :: diff --git a/tests/std/tests/P2465R3_standard_library_modules/custom_format.py b/tests/std/tests/P2465R3_standard_library_modules/custom_format.py index 54567eab7d..d7d6a69fe1 100644 --- a/tests/std/tests/P2465R3_standard_library_modules/custom_format.py +++ b/tests/std/tests/P2465R3_standard_library_modules/custom_format.py @@ -17,10 +17,12 @@ def getBuildSteps(self, test, litConfig, shared): testCpp = test.getSourcePath() sourceDir = os.path.dirname(testCpp) test2Cpp = os.path.join(sourceDir, 'test2.cpp') + test3Cpp = os.path.join(sourceDir, 'test3.cpp') + test4Cpp = os.path.join(sourceDir, 'test4.cpp') classicCpp = os.path.join(sourceDir, 'classic.cpp') # Dependency order is important here: - inputPaths = [stdIxx, stdCompatIxx, testCpp, test2Cpp, classicCpp] + inputPaths = [stdIxx, stdCompatIxx, testCpp, test2Cpp, test3Cpp, test4Cpp, classicCpp] cmd = [test.cxx, *inputPaths, *test.flags, *test.compileFlags] diff --git a/tests/std/tests/P2465R3_standard_library_modules/custombuild.pl b/tests/std/tests/P2465R3_standard_library_modules/custombuild.pl index 72dfff2de1..caece9c6b6 100644 --- a/tests/std/tests/P2465R3_standard_library_modules/custombuild.pl +++ b/tests/std/tests/P2465R3_standard_library_modules/custombuild.pl @@ -16,7 +16,7 @@ () my $stdCompatIxx = "$stlModulesDir\\std.compat.ixx"; # Dependency order is important here: - my @inputPaths = ($stdIxx, $stdCompatIxx, "test.cpp", "test2.cpp", "classic.cpp"); + my @inputPaths = ($stdIxx, $stdCompatIxx, "test.cpp", "test2.cpp", "test3.cpp", "test4.cpp", "classic.cpp"); Run::ExecuteCL(join(" ", @inputPaths, "/Fe$cwd.exe")); } diff --git a/tests/std/tests/P2465R3_standard_library_modules/test.cpp b/tests/std/tests/P2465R3_standard_library_modules/test.cpp index 8621cba0c8..06e295a6ef 100644 --- a/tests/std/tests/P2465R3_standard_library_modules/test.cpp +++ b/tests/std/tests/P2465R3_standard_library_modules/test.cpp @@ -13,12 +13,16 @@ import std; void prepare_test_environment(); void all_std_cmeow_tests(); void test_module_std_compat(); +void test_include_all_then_import_std(); +void test_include_all_then_import_std_compat(); int main() { prepare_test_environment(); // defined in classic.cpp all_cpp_header_tests(); // defined in test_header_units_and_modules.hpp all_std_cmeow_tests(); // defined below test_module_std_compat(); // defined in test2.cpp + test_include_all_then_import_std(); // defined in test3.cpp + test_include_all_then_import_std_compat(); // defined in test4.cpp } void test_std_cassert() { diff --git a/tests/std/tests/P2465R3_standard_library_modules/test3.cpp b/tests/std/tests/P2465R3_standard_library_modules/test3.cpp new file mode 100644 index 0000000000..f826fdc436 --- /dev/null +++ b/tests/std/tests/P2465R3_standard_library_modules/test3.cpp @@ -0,0 +1,30 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#ifdef _MSVC_INTERNAL_TESTING // TRANSITION, VS 2022 17.9 Preview 3 +#include <__msvc_all_public_headers.hpp> +#else // ^^^ no workaround / workaround vvv +#include // intentionally not +#endif // ^^^ workaround ^^^ + +import std; + +// INTENTIONALLY AVOIDED: using namespace std; + +void test_include_all_then_import_std() { + // Verify that std::vector and std::ranges algorithms are available: + std::vector v{31, 41, 59, 26, 53, 58, 97, 93}; + assert(!std::ranges::is_sorted(v)); + std::ranges::sort(v); + assert(std::ranges::is_sorted(v)); + const std::vector sorted{26, 31, 41, 53, 58, 59, 93, 97}; + assert(v == sorted); + + // Verify that the Sufficient Additional Overloads are available for std::sqrt(): + assert(std::sqrt(25.0) == 5.0); + assert(std::sqrt(25.0f) == 5.0f); + assert(std::sqrt(25) == 5.0); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); +} diff --git a/tests/std/tests/P2465R3_standard_library_modules/test4.cpp b/tests/std/tests/P2465R3_standard_library_modules/test4.cpp new file mode 100644 index 0000000000..953ff2a5a9 --- /dev/null +++ b/tests/std/tests/P2465R3_standard_library_modules/test4.cpp @@ -0,0 +1,38 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#ifdef _MSVC_INTERNAL_TESTING // TRANSITION, VS 2022 17.9 Preview 3 +#include <__msvc_all_public_headers.hpp> +#else // ^^^ no workaround / workaround vvv +#include // intentionally not +#endif // ^^^ workaround ^^^ + +import std.compat; + +// INTENTIONALLY AVOIDED: using namespace std; + +void test_include_all_then_import_std_compat() { + // Verify that std::vector and std::ranges algorithms are available: + std::vector v{31, 41, 59, 26, 53, 58, 97, 93}; + assert(!std::ranges::is_sorted(v)); + std::ranges::sort(v); + assert(std::ranges::is_sorted(v)); + const std::vector sorted{26, 31, 41, 53, 58, 59, 93, 97}; + assert(v == sorted); + + // Verify that the Sufficient Additional Overloads are available for std::sqrt(): + assert(std::sqrt(25.0) == 5.0); + assert(std::sqrt(25.0f) == 5.0f); + assert(std::sqrt(25) == 5.0); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + + // Verify that the Sufficient Additional Overloads are available for ::sqrt(): + assert(::sqrt(25.0) == 5.0); + assert(::sqrt(25.0f) == 5.0f); + assert(::sqrt(25) == 5.0); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); +}