diff --git a/stl/inc/__msvc_int128.hpp b/stl/inc/__msvc_int128.hpp index e0ee63d6875..e26d7ead234 100644 --- a/stl/inc/__msvc_int128.hpp +++ b/stl/inc/__msvc_int128.hpp @@ -902,8 +902,6 @@ struct _Unsigned128 : _Base128 { } }; -template -class numeric_limits; template <> class numeric_limits<_Unsigned128> : public _Num_int_base { public: @@ -948,8 +946,6 @@ class numeric_limits<_Unsigned128> : public _Num_int_base { static constexpr int digits10 = 38; }; -template -struct common_type; template struct common_type<_Ty, _Unsigned128> { using type = _Unsigned128; @@ -1266,8 +1262,6 @@ struct _Signed128 : _Base128 { } }; -template -class numeric_limits; template <> class numeric_limits<_Signed128> : public _Num_int_base { public: diff --git a/stl/inc/header-units.json b/stl/inc/header-units.json index f15b818629d..e0c2402da46 100644 --- a/stl/inc/header-units.json +++ b/stl/inc/header-units.json @@ -112,7 +112,7 @@ "typeinfo", "unordered_map", "unordered_set", - "use_ansi.h", + // "use_ansi.h", // internal header, incompatible with being a separate header unit "utility", "valarray", "variant", diff --git a/tests/std/tests/P1502R1_standard_library_header_units/custom_format.py b/tests/std/tests/P1502R1_standard_library_header_units/custom_format.py index 7610705bb87..7008dab9de0 100644 --- a/tests/std/tests/P1502R1_standard_library_header_units/custom_format.py +++ b/tests/std/tests/P1502R1_standard_library_header_units/custom_format.py @@ -1,129 +1,221 @@ # Copyright (c) Microsoft Corporation. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +import json import os +import re from stl.test.format import STLTestFormat, TestStep from stl.test.tests import TestType +# Print noisy progress messages that are useful when working on this script. +noisyProgress = False + + +# P1502R1_standard_library_header_units/test.cpp cites the definition of "importable C++ library headers". +def getImportableCxxLibraryHeaders(): + return [ + 'algorithm', + 'any', + 'array', + 'atomic', + 'barrier', + 'bit', + 'bitset', + 'charconv', + 'chrono', + 'codecvt', + 'compare', + 'complex', + 'concepts', + 'condition_variable', + 'coroutine', + 'deque', + 'exception', + 'execution', + 'filesystem', + 'format', + 'forward_list', + 'fstream', + 'functional', + 'future', + 'initializer_list', + 'iomanip', + 'ios', + 'iosfwd', + 'iostream', + 'istream', + 'iterator', + 'latch', + 'limits', + 'list', + 'locale', + 'map', + 'memory', + 'memory_resource', + 'mutex', + 'new', + 'numbers', + 'numeric', + 'optional', + 'ostream', + 'queue', + 'random', + 'ranges', + 'ratio', + 'regex', + 'scoped_allocator', + 'semaphore', + 'set', + 'shared_mutex', + 'source_location', + 'span', + 'spanstream', + 'sstream', + 'stack', + 'stdexcept', + 'stop_token', + 'streambuf', + 'string', + 'string_view', + 'strstream', + 'syncstream', + 'system_error', + 'thread', + 'tuple', + 'type_traits', + 'typeindex', + 'typeinfo', + 'unordered_map', + 'unordered_set', + 'utility', + 'valarray', + 'variant', + 'vector', + 'version', + ] + + +def loadJsonWithComments(filename): + # This is far from a general-purpose solution (it doesn't attempt to handle block comments like /**/ + # and comments appearing within strings like "cats // dogs"), but it's sufficient for header-units.json. + comment = re.compile('//.*') + with open(filename) as file: + replacedLines = [re.sub(comment, '', line) for line in file] + return json.loads(''.join(replacedLines)) + + +def getAllHeaders(headerUnitsJsonFilename): + buildAsHeaderUnits = loadJsonWithComments(headerUnitsJsonFilename)['BuildAsHeaderUnits'] + + # We want to build everything that's mentioned in header-units.json, plus all of the + # headers that were commented out for providing macros that control header inclusion. + return sorted(set(buildAsHeaderUnits + ['version', 'yvals.h', 'yvals_core.h'])) + + class CustomTestFormat(STLTestFormat): def getBuildSteps(self, test, litConfig, shared): - stlHeaders = [ - 'algorithm', - 'any', - 'array', - 'atomic', - 'barrier', - 'bit', - 'bitset', - 'charconv', - 'chrono', - 'codecvt', - 'compare', - 'complex', - 'concepts', - 'condition_variable', - 'coroutine', - 'deque', - 'exception', - 'execution', - 'filesystem', - 'format', - 'forward_list', - 'fstream', - 'functional', - 'future', - 'initializer_list', - 'iomanip', - 'ios', - 'iosfwd', - 'iostream', - 'istream', - 'iterator', - 'latch', - 'limits', - 'list', - 'locale', - 'map', - 'memory_resource', - 'memory', - 'mutex', - 'new', - 'numbers', - 'numeric', - 'optional', - 'ostream', - 'queue', - 'random', - 'ranges', - 'ratio', - 'regex', - 'scoped_allocator', - 'semaphore', - 'set', - 'shared_mutex', - 'source_location', - 'span', - 'spanstream', - 'sstream', - 'stack', - 'stdexcept', - 'stop_token', - 'streambuf', - 'string_view', - 'string', - 'strstream', - 'syncstream', - 'system_error', - 'thread', - 'tuple', - 'type_traits', - 'typeindex', - 'typeinfo', - 'unordered_map', - 'unordered_set', - 'utility', - 'valarray', - 'variant', - 'vector', - 'version', - ] - outputDir, outputBase = test.getTempPaths() sourcePath = test.getSourcePath() - compileTestCppWithEdg = False - if '/BE' in test.flags: - compileTestCppWithEdg = True - test.flags.remove('/BE') - - if '/BE' in test.compileFlags: - compileTestCppWithEdg = True + compileTestCppWithEdg = '/BE' in test.compileFlags + if compileTestCppWithEdg: test.compileFlags.remove('/BE') - exportHeaderOptions = ['/exportHeader', '/headerName:angle', '/Fo', '/MP'] - headerUnitOptions = [] - for header in stlHeaders: - exportHeaderOptions.append(header) + # This is a list of compiler options to consume the header units that we've built so far. + consumeBuiltHeaderUnits = [] + + # Output files: + objFilenames = [] + + if '/DTEST_TOPO_SORT' in test.compileFlags: # Build deduplicated header units: + # Compiler options, common to both scanning dependencies and building header units. + clOptions = ['/exportHeader', '/headerName:angle', '/translateInclude', '/Fo', '/MP'] + + # Store the list of headers to build. + allHeaders = getAllHeaders(os.path.join(litConfig.cxx_headers, 'header-units.json')) + + # Generate JSON files that record how these headers depend on one another. + if noisyProgress: + print('Scanning dependencies...') + cmd = [test.cxx, *test.flags, *test.compileFlags, *clOptions, '/scanDependencies', '.\\', *allHeaders] + yield TestStep(cmd, shared.execDir, shared.env, False) + + # The JSON files also record what object files will be produced. + # IFC filenames and OBJ filenames follow different patterns. For example: + # produces filesystem.ifc and filesystem.obj + # produces xbit_ops.h.ifc and xbit_ops.obj + # We can easily synthesize IFC filenames, but it's easier to get the OBJ filenames from the JSON files. + + # This dictionary powers the topological sort. + # Key: Header name, e.g. 'bitset'. + # Value: List of dependencies that remain to be built, e.g. ['iosfwd', 'limits', 'xstring']. + remainingDependencies = {} + + # Read the JSON files, storing the results in objFilenames and remainingDependencies. + for hdr in allHeaders: + with open(os.path.join(outputDir, f'{hdr}.module.json')) as file: + jsonObject = json.load(file) + objFilenames.append(jsonObject['rules'][0]['primary-output']) + # TRANSITION, VSO-1466711 fixed in VS 2022 17.2 Preview 2 + # os.path.basename(req['source-path']) should be req['logical-name'] + dep = [os.path.basename(req['source-path']) for req in jsonObject['rules'][0]['requires']] + remainingDependencies[hdr] = dep + + # Build header units in topologically sorted order. + while len(remainingDependencies) > 0: + # When a header has no remaining dependencies, it is ready to be built. + readyToBuild = [hdr for hdr, dep in remainingDependencies.items() if len(dep) == 0] + + # Each layer can be built in parallel. + if noisyProgress: + print('Building deduplicated header units:', ' '.join(readyToBuild)) + cmd = [test.cxx, *test.flags, *test.compileFlags, *clOptions, *consumeBuiltHeaderUnits, *readyToBuild] + yield TestStep(cmd, shared.execDir, shared.env, False) + + # Update remainingDependencies by doing two things. + # hdr, dep is the current key-value pair. + # First, keep `if len(dep) > 0`. (Otherwise, we just built hdr.) + # Second, filter dep, eliminating anything that appears in readyToBuild. (If we're left with + # an empty list, then hdr will be ready to build in the next iteration.) + remainingDependencies = { + hdr: [d for d in dep if d not in readyToBuild] + for hdr, dep in remainingDependencies.items() if len(dep) > 0 + } - headerUnitOptions.append('/headerUnit:angle') - headerUnitOptions.append('{0}={0}.ifc'.format(header)) + # Add compiler options to consume the header units that we just built. + for hdr in readyToBuild: + consumeBuiltHeaderUnits += ['/headerUnit:angle', f'{hdr}={hdr}.ifc'] + else: # Build independent header units: + stlHeaders = getImportableCxxLibraryHeaders() + exportHeaderOptions = ['/exportHeader', '/headerName:angle', '/Fo', '/MP'] + for hdr in stlHeaders: + consumeBuiltHeaderUnits += ['/headerUnit:angle', f'{hdr}={hdr}.ifc'] + objFilenames.append(f'{hdr}.obj') - if not compileTestCppWithEdg: - headerUnitOptions.append(os.path.join(outputDir, header + '.obj')) + if noisyProgress: + print('Building independent header units...') + cmd = [test.cxx, *test.flags, *test.compileFlags, *exportHeaderOptions, *stlHeaders] + yield TestStep(cmd, shared.execDir, shared.env, False) - cmd = [test.cxx, *test.flags, *test.compileFlags, *exportHeaderOptions] + # For convenience, create a library file containing all of the object files that were produced. + libFilename = 'stl_header_units.lib' + if noisyProgress: + print('Creating library...') + cmd = ['lib.exe', '/nologo', f'/out:{libFilename}', *objFilenames] yield TestStep(cmd, shared.execDir, shared.env, False) if compileTestCppWithEdg: test.compileFlags.append('/BE') if TestType.COMPILE in test.testType: - cmd = [test.cxx, '/c', sourcePath, *test.flags, *test.compileFlags, *headerUnitOptions] + cmd = [test.cxx, '/c', sourcePath, *test.flags, *test.compileFlags, *consumeBuiltHeaderUnits] elif TestType.RUN in test.testType: - shared.execFile = outputBase + '.exe' - cmd = [test.cxx, sourcePath, *test.flags, *test.compileFlags, *headerUnitOptions, '/Fe' + shared.execFile, - '/link', *test.linkFlags] + shared.execFile = f'{outputBase}.exe' + cmd = [test.cxx, sourcePath, *test.flags, *test.compileFlags, *consumeBuiltHeaderUnits, libFilename, + f'/Fe{shared.execFile}', '/link', *test.linkFlags] + if noisyProgress: + print('Compiling and running test...') yield TestStep(cmd, shared.execDir, shared.env, False) diff --git a/tests/std/tests/P1502R1_standard_library_header_units/custombuild.pl b/tests/std/tests/P1502R1_standard_library_header_units/custombuild.pl index 3e0497ce828..4e239806000 100644 --- a/tests/std/tests/P1502R1_standard_library_header_units/custombuild.pl +++ b/tests/std/tests/P1502R1_standard_library_header_units/custombuild.pl @@ -44,8 +44,8 @@ () "list", "locale", "map", - "memory_resource", "memory", + "memory_resource", "mutex", "new", "numbers", @@ -69,8 +69,8 @@ () "stdexcept", "stop_token", "streambuf", - "string_view", "string", + "string_view", "strstream", "syncstream", "system_error", diff --git a/tests/std/tests/P1502R1_standard_library_header_units/env.lst b/tests/std/tests/P1502R1_standard_library_header_units/env.lst index e0df24e714c..bf514e1e1c5 100644 --- a/tests/std/tests/P1502R1_standard_library_header_units/env.lst +++ b/tests/std/tests/P1502R1_standard_library_header_units/env.lst @@ -13,6 +13,9 @@ PM_CL="/MD" PM_CL="/MDd" PM_CL="/MT" PM_CL="/MTd" +RUNALL_CROSSLIST +PM_CL="/DTEST_TOPO_SORT" +PM_CL="" # RUNALL_CROSSLIST # PM_CL="" # PM_CL="/analyze:only /analyze:autolog-" # TRANSITION, works correctly but slowly diff --git a/tests/std/tests/P1502R1_standard_library_header_units/test.cpp b/tests/std/tests/P1502R1_standard_library_header_units/test.cpp index f2dd1d14d44..118a06f90d3 100644 --- a/tests/std/tests/P1502R1_standard_library_header_units/test.cpp +++ b/tests/std/tests/P1502R1_standard_library_header_units/test.cpp @@ -285,11 +285,13 @@ int main() { assert(!ep); } +#ifndef TEST_TOPO_SORT // TRANSITION, VSO-1471382 (error C2672: 'count_if': no matching overloaded function found) { puts("Testing ."); constexpr int arr[]{11, 0, 22, 0, 33, 0, 44, 0, 55}; assert(count(execution::par, begin(arr), end(arr), 0) == 4); } +#endif // ^^^ no workaround ^^^ { puts("Testing ."); @@ -319,6 +321,7 @@ int main() { assert(!f.is_open()); } +#ifndef TEST_TOPO_SORT // TRANSITION, VSO-1471374 (fatal error C1116: unrecoverable error importing module) { puts("Testing ."); function f{multiplies{}}; @@ -329,6 +332,7 @@ int main() { assert(b(3) == 33); static_assert(b(3) == 33); } +#endif // ^^^ no workaround ^^^ { puts("Testing ."); @@ -861,6 +865,7 @@ int main() { assert(this_thread::get_id() != thread::id{}); } +#ifndef TEST_TOPO_SORT // TRANSITION, VSO-1471374 (fatal error C1116: unrecoverable error importing module) { puts("Testing ."); constexpr tuple t{1729, 'c', 1.25}; @@ -871,6 +876,7 @@ int main() { static_assert(get(t) == 'c'); static_assert(get(t) == 1.25); } +#endif // ^^^ no workaround ^^^ { puts("Testing ."); diff --git a/tests/utils/stl/util.py b/tests/utils/stl/util.py index d9e0e97b2e3..e1b3b07760e 100644 --- a/tests/utils/stl/util.py +++ b/tests/utils/stl/util.py @@ -45,10 +45,11 @@ def nullContext(value): def makeReport(cmd, out, err, rc): report = "Command: \"%s\"\n" % "\" \"".join(cmd) report += "Exit Code: %d\n" % rc + # Replacing CRLFs with LFs avoids ugly double newlines when this is displayed in Azure Pipelines. if out: - report += "Standard Output:\n--\n%s--\n" % out + report += "Standard Output:\n--\n%s--\n" % out.replace("\r\n", "\n") if err: - report += "Standard Error:\n--\n%s--\n" % err + report += "Standard Error:\n--\n%s--\n" % err.replace("\r\n", "\n") report += '\n' return report diff --git a/tools/scripts/print_failures.py b/tools/scripts/print_failures.py index d4120d81090..d345338dd22 100644 --- a/tools/scripts/print_failures.py +++ b/tools/scripts/print_failures.py @@ -9,15 +9,15 @@ if len(sys.argv) != 2: sys.exit(f"Usage: python {sys.argv[0]} TEST_LOG_FILENAME") -test_log = json.load(open(sys.argv[1])) - -for result in test_log["tests"]: - if not result["code"] in ["PASS", "UNSUPPORTED", "XFAIL"]: - print("code: {}".format(result["code"])) - # Ignore result["elapsed"]. - print("name: {}".format(result["name"])) - # The JSON contains embedded CRLFs (which aren't affected by opening the file in text mode). - # If we don't replace these CRLFs with LFs here, this script will appear to be okay in the console, - # but redirecting it to a file will result in ugly double newlines. - print("output: {}".format(result["output"].replace("\r\n", "\n"))) - print("==================================================") +with open(sys.argv[1]) as file: + test_log = json.load(file) + for result in test_log["tests"]: + if not result["code"] in ["PASS", "UNSUPPORTED", "XFAIL"]: + print("code: {}".format(result["code"])) + # Ignore result["elapsed"]. + print("name: {}".format(result["name"])) + # The JSON contains embedded CRLFs (which aren't affected by opening the file in text mode). + # If we don't replace these CRLFs with LFs here, this script will appear to be okay in the console, + # but redirecting it to a file will result in ugly double newlines. + print("output: {}".format(result["output"].replace("\r\n", "\n"))) + print("==================================================")