Repository: microsoft/terminal (language: cpp)

Issue:
[MSVC][std:c++latest] terminal failed to build with /std:c++latest on MSVC
### Windows Terminal version

_No response_

### Windows build number

10.0.19041.0

### Other Software

_No response_

### Steps to reproduce

The MSVC team recently added terminal as part of RWC testing. But we found some errors when building terminal with '/std:c++latest'.

**repro steps:**
1. `git clone https://github.com/microsoft/terminal.git F:\gitP\microsoft\terminal`
2. `cd F:\gitP\microsoft\terminal`
3. `set _CL_= /std:c++latest`
4. `set _CL_=/wd4189 %_CL_%`
5. `msbuild /m /p:Platform=x64 /p:Configuration=Release /p:WindowsTargetPlatformVersion=10.0.20348.0 OpenConsole.sln /t:Restore /t:Rebuild`


### Expected Behavior

Terminal could build successfully with '/std:c++latest'.

### Actual Behavior

precomp.cpp
C:\Program Files (x86)\Windows Kits\10\include\10.0.19041.0\winrt\wrl\module.h(1275,10): error C2220: the following warning is treated as an error
C:\Program Files (x86)\Windows Kits\10\include\10.0.19041.0\winrt\wrl\module.h(1275,10): warning C5104: found 'L#serverName' in macro replacement list, did you mean 'L""#serverName'?
C:\Program Files (x86)\Windows Kits\10\include\10.0.19041.0\winrt\wrl\module.h(1275,10): note: to simplify migration, consider the temporary use of /Wv:18 flag with the version of the compiler with which you used to build without warnings
F:\gitP\microsoft\terminal\src\inc\til\bit.h(12,1): fatal error C1021: invalid preprocessor command 'warning'
[build.log](https://github.com/microsoft/terminal/files/8086582/build.log)


Discussion / hints:
Note: It's lucky that the warning C5104 disappeared with Windows SDK 10.0.20348.0.
> The MSVC team recently added terminal as part of RWC testing

Thanks!

> `winrt\wrl\module.h(1275,10): warning C5104`

welp, we'll have to just suppress that one, since we don't own WRL


> `src\inc\til\bit.h(12,1): fatal error C1021`

okay yea that one's on us 😅 


Fix patch:
diff --git a/dep/CLI11/CLI11.hpp b/dep/CLI11/CLI11.hpp
index a7efa607c92..9e502bf960e 100644
--- a/dep/CLI11/CLI11.hpp
+++ b/dep/CLI11/CLI11.hpp
@@ -1,11 +1,11 @@
 #pragma once
 
-// CLI11: Version 1.9.0
+// CLI11: Version 1.9.1
 // Originally designed by Henry Schreiner
 // https://github.com/CLIUtils/CLI11
 //
 // This is a standalone header file generated by MakeSingleHeader.py in CLI11/scripts
-// from: v1.9.0
+// from: v1.9.1
 //
 // From LICENSE:
 //
@@ -60,14 +60,14 @@
 #include <utility>
 #include <vector>
 
-// Verbatim copy from CLI/Version.hpp:
+// Verbatim copy from Version.hpp:
 
 #define CLI11_VERSION_MAJOR 1
 #define CLI11_VERSION_MINOR 9
-#define CLI11_VERSION_PATCH 0
-#define CLI11_VERSION "1.9.0"
+#define CLI11_VERSION_PATCH 1
+#define CLI11_VERSION "1.9.1"
 
-// Verbatim copy from CLI/Macros.hpp:
+// Verbatim copy from Macros.hpp:
 
 // The following version macro is very similar to the one in PyBind11
 #if !(defined(_MSC_VER) && __cplusplus == 199711L) && !defined(__INTEL_COMPILER)
@@ -102,7 +102,7 @@
 #define CLI11_DEPRECATED(reason) __attribute__((deprecated(reason)))
 #endif
 
-// Verbatim copy from CLI/Validators.hpp:
+// Verbatim copy from Validators.hpp:
 
 // C standard library
 // Only needed for existence checking
@@ -114,7 +114,14 @@
 #else
 #include <filesystem>
 #if defined __cpp_lib_filesystem && __cpp_lib_filesystem >= 201703
+#if defined _GLIBCXX_RELEASE && _GLIBCXX_RELEASE >= 9
 #define CLI11_HAS_FILESYSTEM 1
+#elif defined(__GLIBCXX__)
+// if we are using gcc and Version <9 default to no filesystem
+#define CLI11_HAS_FILESYSTEM 0
+#else
+#define CLI11_HAS_FILESYSTEM 1
+#endif
 #else
 #define CLI11_HAS_FILESYSTEM 0
 #endif
@@ -129,11 +136,11 @@
 #include <sys/types.h>
 #endif
 
-// From CLI/Version.hpp:
+// From Version.hpp:
 
-// From CLI/Macros.hpp:
+// From Macros.hpp:
 
-// From CLI/StringTools.hpp:
+// From StringTools.hpp:
 
 namespace CLI
 {
@@ -570,7 +577,7 @@ namespace CLI
 
 } // namespace CLI
 
-// From CLI/Error.hpp:
+// From Error.hpp:
 
 namespace CLI
 {
@@ -844,11 +851,11 @@ public:
     {
         CLI11_ERROR_DEF(ParseError, ArgumentMismatch)
         CLI11_ERROR_SIMPLE(ArgumentMismatch)
-        ArgumentMismatch(std::string name, int expected, std::size_t recieved) :
+        ArgumentMismatch(std::string name, int expected, std::size_t received) :
             ArgumentMismatch(expected > 0 ? ("Expected exactly " + std::to_string(expected) + " arguments to " + name +
-                                             ", got " + std::to_string(recieved)) :
+                                             ", got " + std::to_string(received)) :
                                             ("Expected at least " + std::to_string(-expected) + " arguments to " + name +
-                                             ", got " + std::to_string(recieved)),
+                                             ", got " + std::to_string(received)),
                              ExitCodes::ArgumentMismatch) {}
 
         static ArgumentMismatch AtLeast(std::string name, int num, std::size_t received)
@@ -949,7 +956,7 @@ public:
 
 } // namespace CLI
 
-// From CLI/TypeTools.hpp:
+// From TypeTools.hpp:
 
 namespace CLI
 {
@@ -1237,16 +1244,26 @@ namespace CLI
         };
 
         /// Convert an object to a string (directly forward if this can become a string)
-        template<typename T, enable_if_t<std::is_constructible<std::string, T>::value, detail::enabler> = detail::dummy>
+        template<typename T, enable_if_t<std::is_convertible<T, std::string>::value, detail::enabler> = detail::dummy>
         auto to_string(T&& value) -> decltype(std::forward<T>(value))
         {
             return std::forward<T>(value);
         }
 
+        /// Construct a string from the object
+        template<typename T,
+                 enable_if_t<std::is_constructible<std::string, T>::value && !std::is_convertible<T, std::string>::value,
+                             detail::enabler> = detail::dummy>
+        std::string to_string(const T& value)
+        {
+            return std::string(value);
+        }
+
         /// Convert an object to a string (streaming must be supported for that type)
         template<typename T,
-                 enable_if_t<!std::is_constructible<std::string, T>::value && is_ostreamable<T>::value, detail::enabler> =
-                     detail::dummy>
+                 enable_if_t<!std::is_convertible<std::string, T>::value && !std::is_constructible<std::string, T>::value &&
+                                 is_ostreamable<T>::value,
+                             detail::enabler> = detail::dummy>
         std::string to_string(T&& value)
         {
             std::stringstream stream;
@@ -1624,7 +1641,7 @@ namespace CLI
         // Lexical cast
 
         /// Convert a flag into an integer value  typically binary flags
-        inline int64_t to_flag_value(std::string val)
+        inline std::int64_t to_flag_value(std::string val)
         {
             static const std::string trueString("true");
             static const std::string falseString("false");
@@ -1637,12 +1654,12 @@ namespace CLI
                 return -1;
             }
             val = detail::to_lower(val);
-            int64_t ret;
+            std::int64_t ret;
             if (val.size() == 1)
             {
                 if (val[0] >= '1' && val[0] <= '9')
                 {
-                    return (static_cast<int64_t>(val[0]) - '0');
+                    return (static_cast<std::int64_t>(val[0]) - '0');
                 }
                 switch (val[0])
                 {
@@ -2127,7 +2144,7 @@ namespace CLI
                  enable_if_t<std::is_integral<T>::value && std::is_unsigned<T>::value, detail::enabler> = detail::dummy>
         void sum_flag_vector(const std::vector<std::string>& flags, T& output)
         {
-            int64_t count{ 0 };
+            std::int64_t count{ 0 };
             for (auto& flag : flags)
             {
                 count += detail::to_flag_value(flag);
@@ -2144,7 +2161,7 @@ namespace CLI
                  enable_if_t<std::is_integral<T>::value && std::is_signed<T>::value, detail::enabler> = detail::dummy>
         void sum_flag_vector(const std::vector<std::string>& flags, T& output)
         {
-            int64_t count{ 0 };
+            std::int64_t count{ 0 };
             for (auto& flag : flags)
             {
                 count += detail::to_flag_value(flag);
@@ -2155,7 +2172,7 @@ namespace CLI
     } // namespace detail
 } // namespace CLI
 
-// From CLI/Split.hpp:
+// From Split.hpp:
 
 namespace CLI
 {
@@ -2307,7 +2324,7 @@ namespace CLI
     } // namespace detail
 } // namespace CLI
 
-// From CLI/ConfigFwd.hpp:
+// From ConfigFwd.hpp:
 
 namespace CLI
 {
@@ -2436,7 +2453,7 @@ namespace CLI
     };
 } // namespace CLI
 
-// From CLI/Validators.hpp:
+// From Validators.hpp:
 
 namespace CLI
 {
@@ -2504,7 +2521,7 @@ namespace CLI
                 }
             }
             return retstring;
-        };
+        }
 
         /// This is the required operator for a Validator - provided to help
         /// users (CLI11 uses the member `func` directly)
@@ -2512,7 +2529,7 @@ namespace CLI
         {
             std::string value = str;
             return (active_) ? func_(value) : std::string{};
-        };
+        }
 
         /// Specify the type string
         Validator& description(std::string validator_desc)
@@ -2576,14 +2593,14 @@ namespace CLI
         {
             application_index_ = app_index;
             return *this;
-        };
+        }
         /// Specify the application index of a validator
         Validator application_index(int app_index) const
         {
             Validator newval(*this);
             newval.application_index_ = app_index;
             return newval;
-        };
+        }
         /// Get the current value of the application index
         int get_application_index() const { return application_index_; }
         /// Get a boolean if the validator is active
@@ -2699,7 +2716,7 @@ namespace CLI
         /// CLI enumeration of different file types
         enum class path_type
         {
-            nonexistant,
+            nonexistent,
             file,
             directory
         };
@@ -2712,13 +2729,13 @@ namespace CLI
             auto stat = std::filesystem::status(file, ec);
             if (ec)
             {
-                return path_type::nonexistant;
+                return path_type::nonexistent;
             }
             switch (stat.type())
             {
             case std::filesystem::file_type::none:
             case std::filesystem::file_type::not_found:
-                return path_type::nonexistant;
+                return path_type::nonexistent;
             case std::filesystem::file_type::directory:
                 return path_type::directory;
             case std::filesystem::file_type::symlink:
@@ -2749,7 +2766,7 @@ namespace CLI
                 return ((buffer.st_mode & S_IFDIR) != 0) ? path_type::directory : path_type::file;
             }
 #endif
-            return path_type::nonexistant;
+            return path_type::nonexistent;
         }
 #endif
         /// Check for an existing file (returns error message if check fails)
@@ -2761,7 +2778,7 @@ namespace CLI
             {
                 func_ = [](std::string& filename) {
                     auto path_result = check_path(filename.c_str());
-                    if (path_result == path_type::nonexistant)
+                    if (path_result == path_type::nonexistent)
                     {
                         return "File does not exist: " + filename;
                     }
@@ -2783,7 +2800,7 @@ namespace CLI
             {
                 func_ = [](std::string& filename) {
                     auto path_result = check_path(filename.c_str());
-                    if (path_result == path_type::nonexistant)
+                    if (path_result == path_type::nonexistent)
                     {
                         return "Directory does not exist: " + filename;
                     }
@@ -2805,7 +2822,7 @@ namespace CLI
             {
                 func_ = [](std::string& filename) {
                     auto path_result = check_path(filename.c_str());
-                    if (path_result == path_type::nonexistant)
+                    if (path_result == path_type::nonexistent)
                     {
                         return "Path does not exist: " + filename;
                     }
@@ -2823,7 +2840,7 @@ namespace CLI
             {
                 func_ = [](std::string& filename) {
                     auto path_result = check_path(filename.c_str());
-                    if (path_result != path_type::nonexistant)
+                    if (path_result != path_type::nonexistent)
                     {
                         return "Path already exists: " + filename;
                     }
@@ -3310,7 +3327,7 @@ namespace CLI
             // if the type does not have first_type and second_type, these are both value_type
             using element_t = typename detail::element_type<T>::type; // Removes (smart) pointers if needed
             using item_t = typename detail::pair_adaptor<element_t>::first_type; // Is value_type if not a map
-            using local_item_t = typename IsMemberType<item_t>::type; // This will convert bad types to good ones
+            using local_item_t = typename IsMemberType<item_t>::type; // Will convert bad types to good ones
                 // (const char * to std::string)
 
             // Make a local copy of the filter function, using a std::function if not one already
@@ -3381,10 +3398,9 @@ namespace CLI
             // if the type does not have first_type and second_type, these are both value_type
             using element_t = typename detail::element_type<T>::type; // Removes (smart) pointers if needed
             using item_t = typename detail::pair_adaptor<element_t>::first_type; // Is value_type if not a map
-            using local_item_t = typename IsMemberType<item_t>::type; // This will convert bad types to good ones
+            using local_item_t = typename IsMemberType<item_t>::type; // Will convert bad types to good ones
                 // (const char * to std::string)
-            using iteration_type_t = typename detail::pair_adaptor<element_t>::value_type; // the type of the object pair //
-                // the type of the object pair
+            using iteration_type_t = typename detail::pair_adaptor<element_t>::value_type; // the type of the object pair
 
             // Make a local copy of the filter function, using a std::function if not one already
             std::function<local_item_t(local_item_t)> filter_fn = filter_function;
@@ -3625,7 +3641,7 @@ namespace CLI
     class AsSizeValue : public AsNumberWithUnit
     {
     public:
-        using result_t = uint64_t;
+        using result_t = std::uint64_t;
 
         /// If kb_is_1000 is true,
         /// interpret 'kb', 'k' as 1000 and 'kib', 'ki' as 1024
@@ -3721,7 +3737,7 @@ namespace CLI
 
 } // namespace CLI
 
-// From CLI/FormatterFwd.hpp:
+// From FormatterFwd.hpp:
 
 namespace CLI
 {
@@ -3735,9 +3751,9 @@ namespace CLI
 
     enum class AppFormatMode
     {
-        Normal, //< The normal, detailed help
-        All, //< A fully expanded help
-        Sub, //< Used when printed as part of expanded subcommand
+        Normal, ///< The normal, detailed help
+        All, ///< A fully expanded help
+        Sub, ///< Used when printed as part of expanded subcommand
     };
 
     /// This is the minimum requirements to run a formatter.
@@ -3897,7 +3913,7 @@ namespace CLI
 
 } // namespace CLI
 
-// From CLI/Option.hpp:
+// From Option.hpp:
 
 namespace CLI
 {
@@ -4441,7 +4457,7 @@ namespace CLI
         template<typename T = App>
         Option* needs(std::string opt_name)
         {
-            auto opt = dynamic_cast<T*>(parent_)->get_option_no_throw(opt_name);
+            auto opt = static_cast<T*>(parent_)->get_option_no_throw(opt_name);
             if (opt == nullptr)
             {
                 throw IncorrectConstruction::MissingOption(opt_name);
@@ -4492,7 +4508,7 @@ namespace CLI
         template<typename T = App>
         Option* excludes(std::string opt_name)
         {
-            auto opt = dynamic_cast<T*>(parent_)->get_option_no_throw(opt_name);
+            auto opt = static_cast<T*>(parent_)->get_option_no_throw(opt_name);
             if (opt == nullptr)
             {
                 throw IncorrectConstruction::MissingOption(opt_name);
@@ -4538,7 +4554,7 @@ namespace CLI
             if (!ignore_case_ && value)
             {
                 ignore_case_ = value;
-                auto* parent = dynamic_cast<T*>(parent_);
+                auto* parent = static_cast<T*>(parent_);
                 for (const Option_p& opt : parent->options_)
                 {
                     if (opt.get() == this)
@@ -4570,7 +4586,7 @@ namespace CLI
             if (!ignore_underscore_ && value)
             {
                 ignore_underscore_ = value;
-                auto* parent = dynamic_cast<T*>(parent_);
+                auto* parent = static_cast<T*>(parent_);
                 for (const Option_p& opt : parent->options_)
                 {
                     if (opt.get() == this)
@@ -4698,9 +4714,9 @@ namespace CLI
         /// Will include / prefer the positional name if positional is true.
         /// If all_options is false, pick just the most descriptive name to show.
         /// Use `get_name(true)` to get the positional name (replaces `get_pname`)
-        std::string get_name(bool positional = false, //<[input] Show the positional name
-                             bool all_options = false //<[input] Show every option
-                             ) const
+        std::string get_name(bool positional = false, ///< Show the positional name
+                             bool all_options = false ///< Show every option
+        ) const
         {
             if (get_group().empty())
                 return {}; // Hidden
@@ -5000,7 +5016,7 @@ namespace CLI
                 {
                     if (!default_str_.empty())
                     {
-                        //_add_results takes an rvalue only
+                        // _add_results takes an rvalue only
                         _add_result(std::string(default_str_), res);
                         _validate_results(res);
                         results_t extra;
@@ -5379,7 +5395,7 @@ namespace CLI
 
 } // namespace CLI
 
-// From CLI/App.hpp:
+// From App.hpp:
 
 namespace CLI
 {
@@ -6221,8 +6237,9 @@ namespace CLI
         }
 
         /// Vector version to capture multiple flags.
-        template<typename T,
-                 enable_if_t<!std::is_assignable<std::function<void(int64_t)>, T>::value, detail::enabler> = detail::dummy>
+        template<
+            typename T,
+            enable_if_t<!std::is_assignable<std::function<void(std::int64_t)>, T>::value, detail::enabler> = detail::dummy>
         Option* add_flag(std::string flag_name,
                          std::vector<T>& flag_results, ///< A vector of values with the flag results
                          std::string flag_description = "")
@@ -6260,11 +6277,11 @@ namespace CLI
 
         /// Add option for callback with an integer value
         Option* add_flag_function(std::string flag_name,
-                                  std::function<void(int64_t)> function, ///< A function to call, void(int)
+                                  std::function<void(std::int64_t)> function, ///< A function to call, void(int)
                                   std::string flag_description = "")
         {
             CLI::callback_t fun = [function](const CLI::results_t& res) {
-                int64_t flag_count = 0;
+                std::int64_t flag_count = 0;
                 detail::sum_flag_vector(res, flag_count);
                 function(flag_count);
                 return true;
@@ -6276,7 +6293,7 @@ namespace CLI
 #ifdef CLI11_CPP14
         /// Add option for callback (C++14 or better only)
         Option* add_flag(std::string flag_name,
-                         std::function<void(int64_t)> function, ///< A function to call, void(int64_t)
+                         std::function<void(std::int64_t)> function, ///< A function to call, void(std::int64_t)
                          std::string flag_description = "")
         {
             return add_flag_function(std::move(flag_name), std::move(function), std::move(flag_description));
@@ -6452,7 +6469,7 @@ namespace CLI
         template<typename T = Option_group>
         T* add_option_group(std::string group_name, std::string group_description = "")
         {
-            auto option_group = std::make_shared<T>(std::move(group_description), group_name, nullptr);
+            auto option_group = std::make_shared<T>(std::move(group_description), group_name, this);
             auto ptr = option_group.get();
             // move to App_p for overload resolution on older gcc versions
             App_p app_ptr = std::dynamic_pointer_cast<App>(option_group);
@@ -6461,7 +6478,7 @@ namespace CLI
         }
 
         ///@}
-        /// @name Subcommmands
+        /// @name Subcommands
         ///@{
 
         /// Add a subcommand. Inherits INHERITABLE and OptionDefaults, and help flag
@@ -6837,16 +6854,16 @@ namespace CLI
         int exit(const Error& e, std::ostream& out = std::cout, std::ostream& err = std::cerr) const
         {
             /// Avoid printing anything if this is a CLI::RuntimeError
-            if (dynamic_cast<const CLI::RuntimeError*>(&e) != nullptr)
+            if (e.get_name() == "RuntimeError")
                 return e.get_exit_code();
 
-            if (dynamic_cast<const CLI::CallForHelp*>(&e) != nullptr)
+            if (e.get_name() == "CallForHelp")
             {
                 out << help();
                 return e.get_exit_code();
             }
 
-            if (dynamic_cast<const CLI::CallForAllHelp*>(&e) != nullptr)
+            if (e.get_name() == "CallForAllHelp")
             {
                 out << help("", AppFormatMode::All);
                 return e.get_exit_code();
@@ -7080,7 +7097,12 @@ namespace CLI
         /// Access the config formatter as a configBase pointer
         std::shared_ptr<ConfigBase> get_config_formatter_base() const
         {
+            // This is safer as a dynamic_cast if we have RTTI, as Config -> ConfigBase
+#if defined(__cpp_rtti) || (defined(__GXX_RTTI) && __GXX_RTTI) || (defined(_HAS_STATIC_RTTI) && (_HAS_STATIC_RTTI == 0))
             return std::dynamic_pointer_cast<ConfigBase>(config_formatter_);
+#else
+            return std::static_pointer_cast<ConfigBase>(config_formatter_);
+#endif
         }
 
         /// Get the app or subcommand description
@@ -8908,6 +8930,21 @@ namespace CLI
         /// This class is simply to allow tests access to App's protected functions
         struct AppFriend
         {
+#ifdef CLI11_CPP14
+            /// Wrap _parse_short, perfectly forward arguments and return
+            template<typename... Args>
+            static decltype(auto) parse_arg(App* app, Args&&... args)
+            {
+                return app->_parse_arg(std::forward<Args>(args)...);
+            }
+
+            /// Wrap _parse_subcommand, perfectly forward arguments and return
+            template<typename... Args>
+            static decltype(auto) parse_subcommand(App* app, Args&&... args)
+            {
+                return app->_parse_subcommand(std::forward<Args>(args)...);
+            }
+#else
             /// Wrap _parse_short, perfectly forward arguments and return
             template<typename... Args>
             static auto parse_arg(App* app, Args&&... args) ->
@@ -8923,6 +8960,7 @@ namespace CLI
             {
                 return app->_parse_subcommand(std::forward<Args>(args)...);
             }
+#endif
             /// Wrap the fallthrough parent function to make sure that is working correctly
             static App* get_fallthrough_parent(App* app) { return app->_get_fallthrough_parent(); }
         };
@@ -8930,7 +8968,7 @@ namespace CLI
 
 } // namespace CLI
 
-// From CLI/Config.hpp:
+// From Config.hpp:
 
 namespace CLI
 {
@@ -9360,7 +9398,7 @@ namespace CLI
 
 } // namespace CLI
 
-// From CLI/Formatter.hpp:
+// From Formatter.hpp:
 
 namespace CLI
 {
diff --git a/dep/CLI11/README.md b/dep/CLI11/README.md
index a6e04c18013..023ffc1e3b5 100644
--- a/dep/CLI11/README.md
+++ b/dep/CLI11/README.md
@@ -1,5 +1,4 @@
 # CLI11
 
-Taken from [release v1.9.0](https://github.com/CLIUtils/CLI11/releases/tag/v1.9.0), source commit
-[dd0d8e4](https://github.com/CLIUtils/CLI11/commit/dd0d8e4fe729e5b1110232c7a5c9566dad884686)
-
+Taken from [release v1.9.1](https://github.com/CLIUtils/CLI11/releases/tag/v1.9.1), source commit
+[5cb3efa](https://github.com/CLIUtils/CLI11/commit/5cb3efabce007c3a0230e4cc2e27da491c646b6c)
diff --git a/src/cascadia/LocalTests_SettingsModel/ColorSchemeTests.cpp b/src/cascadia/LocalTests_SettingsModel/ColorSchemeTests.cpp
index c13bcbdc8b6..fac134c9519 100644
--- a/src/cascadia/LocalTests_SettingsModel/ColorSchemeTests.cpp
+++ b/src/cascadia/LocalTests_SettingsModel/ColorSchemeTests.cpp
@@ -84,7 +84,7 @@ namespace SettingsModelLocalTests
 
         for (size_t i = 0; i < expectedCampbellTable.size(); i++)
         {
-            const auto& expected = expectedCampbellTable.at(i);
+            const til::color expected{ expectedCampbellTable.at(i) };
             const til::color actual{ scheme->Table().at(static_cast<uint32_t>(i)) };
             VERIFY_ARE_EQUAL(expected, actual);
         }
diff --git a/src/cascadia/LocalTests_SettingsModel/TerminalSettingsTests.cpp b/src/cascadia/LocalTests_SettingsModel/TerminalSettingsTests.cpp
index ad2463bbb70..4f064a0e6cb 100644
--- a/src/cascadia/LocalTests_SettingsModel/TerminalSettingsTests.cpp
+++ b/src/cascadia/LocalTests_SettingsModel/TerminalSettingsTests.cpp
@@ -795,11 +795,11 @@ namespace SettingsModelLocalTests
         const auto terminalSettings4 = createTerminalSettings(activeProfiles.GetAt(4), colorSchemes);
         const auto terminalSettings5 = createTerminalSettings(activeProfiles.GetAt(5), colorSchemes);
 
-        VERIFY_ARE_EQUAL(RGB(0x12, 0x34, 0x56), terminalSettings0->CursorColor()); // from color scheme
+        VERIFY_ARE_EQUAL(til::color(0x12, 0x34, 0x56), terminalSettings0->CursorColor()); // from color scheme
         VERIFY_ARE_EQUAL(DEFAULT_CURSOR_COLOR, terminalSettings1->CursorColor()); // default
-        VERIFY_ARE_EQUAL(RGB(0x23, 0x45, 0x67), terminalSettings2->CursorColor()); // from profile (trumps color scheme)
-        VERIFY_ARE_EQUAL(RGB(0x34, 0x56, 0x78), terminalSettings3->CursorColor()); // from profile (not set in color scheme)
-        VERIFY_ARE_EQUAL(RGB(0x45, 0x67, 0x89), terminalSettings4->CursorColor()); // from profile (no color scheme)
+        VERIFY_ARE_EQUAL(til::color(0x23, 0x45, 0x67), terminalSettings2->CursorColor()); // from profile (trumps color scheme)
+        VERIFY_ARE_EQUAL(til::color(0x34, 0x56, 0x78), terminalSettings3->CursorColor()); // from profile (not set in color scheme)
+        VERIFY_ARE_EQUAL(til::color(0x45, 0x67, 0x89), terminalSettings4->CursorColor()); // from profile (no color scheme)
         VERIFY_ARE_EQUAL(DEFAULT_CURSOR_COLOR, terminalSettings5->CursorColor()); // default
     }
 
diff --git a/src/cascadia/TerminalApp/AppLogic.cpp b/src/cascadia/TerminalApp/AppLogic.cpp
index 0d8cb34fa2f..fe6636a4f93 100644
--- a/src/cascadia/TerminalApp/AppLogic.cpp
+++ b/src/cascadia/TerminalApp/AppLogic.cpp
@@ -1007,7 +1007,7 @@ namespace winrt::TerminalApp::implementation
         const auto package{ GetCurrentPackageNoThrow() };
         if (package == nullptr)
         {
-            return;
+            co_return;
         }
 
         const auto tryEnableStartupTask = _settings.GlobalSettings().StartOnUserLogin();
diff --git a/src/cascadia/TerminalApp/TabBase.cpp b/src/cascadia/TerminalApp/TabBase.cpp
index 6cda2158ae3..6667f760ea7 100644
--- a/src/cascadia/TerminalApp/TabBase.cpp
+++ b/src/cascadia/TerminalApp/TabBase.cpp
@@ -176,7 +176,7 @@ namespace winrt::TerminalApp::implementation
 
         if (_keyChord == keyChordText)
         {
-            return;
+            co_return;
         }
 
         _keyChord = keyChordText;
diff --git a/src/cascadia/TerminalApp/TerminalTab.cpp b/src/cascadia/TerminalApp/TerminalTab.cpp
index bacba947c5f..5dc52596177 100644
--- a/src/cascadia/TerminalApp/TerminalTab.cpp
+++ b/src/cascadia/TerminalApp/TerminalTab.cpp
@@ -274,7 +274,7 @@ namespace winrt::TerminalApp::implementation
         // Don't reload our icon if it hasn't changed.
         if (iconPath == _lastIconPath)
         {
-            return;
+            co_return;
         }
 
         _lastIconPath = iconPath;
@@ -283,7 +283,7 @@ namespace winrt::TerminalApp::implementation
         // for when we show the icon again)
         if (_iconHidden)
         {
-            return;
+            co_return;
         }
 
         auto weakThis{ get_weak() };
diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp
index b6bb9e6b98b..d329f1132f1 100644
--- a/src/cascadia/TerminalControl/TermControl.cpp
+++ b/src/cascadia/TerminalControl/TermControl.cpp
@@ -2274,7 +2274,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
     {
         if (_IsClosing())
         {
-            return;
+            co_return;
         }
 
         if (e.DataView().Contains(StandardDataFormats::ApplicationLink()))
diff --git a/src/cascadia/TerminalSettingsModel/FileUtils.cpp b/src/cascadia/TerminalSettingsModel/FileUtils.cpp
index 4535c23be06..30104ff28a6 100644
--- a/src/cascadia/TerminalSettingsModel/FileUtils.cpp
+++ b/src/cascadia/TerminalSettingsModel/FileUtils.cpp
@@ -12,7 +12,7 @@
 #include <sddl.h>
 #include <wil/token_helpers.h>
 
-static constexpr std::string_view Utf8Bom{ u8"\uFEFF" };
+static constexpr std::string_view Utf8Bom{ "\xEF\xBB\xBF", 3 };
 static constexpr std::wstring_view UnpackagedSettingsFolderName{ L"Microsoft\\Windows Terminal\\" };
 
 namespace winrt::Microsoft::Terminal::Settings::Model
diff --git a/src/common.build.pre.props b/src/common.build.pre.props
index 30d7fb285fc..4d494f911ab 100644
--- a/src/common.build.pre.props
+++ b/src/common.build.pre.props
@@ -118,7 +118,8 @@
       <RuntimeTypeInfo>false</RuntimeTypeInfo>
       <ConformanceMode>true</ConformanceMode>
       <UseStandardPreprocessor>true</UseStandardPreprocessor>
-      <LanguageStandard>stdcpp17</LanguageStandard>
+      <LanguageStandard Condition="$(MSBuildVersion) &lt; '17.0.0'">stdcpp17</LanguageStandard>
+      <LanguageStandard Condition="$(MSBuildVersion) &gt;= '17.0.0'">stdcpp20</LanguageStandard>
       <LanguageStandard_C>stdc17</LanguageStandard_C>
       <AdditionalOptions>%(AdditionalOptions) /utf-8 /Zc:externConstexpr /Zc:lambda /Zc:throwingNew</AdditionalOptions>
       <ControlFlowGuard>Guard</ControlFlowGuard>
diff --git a/src/host/ft_host/InitTests.cpp b/src/host/ft_host/InitTests.cpp
index ea738dd8fa3..f3d062c8878 100644
--- a/src/host/ft_host/InitTests.cpp
+++ b/src/host/ft_host/InitTests.cpp
@@ -25,7 +25,7 @@ static FILE* std_in = nullptr;
 // This will automatically try to terminate the job object (and all of the
 // binaries under test that are children) whenever this class gets shut down.
 // also closes the FILE pointers created by reopening stdin and stdout.
-auto OnAppExitKillJob = wil::scope_exit([&] {
+auto OnAppExitKillJob = wil::scope_exit([] {
     if (std_out != nullptr)
     {
         fclose(std_out);
diff --git a/src/inc/til/bit.h b/src/inc/til/bit.h
index fd8d9c44ff3..dc15d4bce44 100644
--- a/src/inc/til/bit.h
+++ b/src/inc/til/bit.h
@@ -8,9 +8,7 @@ namespace til
     template<class To, class From, std::enable_if_t<std::conjunction_v<std::bool_constant<sizeof(To) == sizeof(From)>, std::is_trivially_copyable<To>, std::is_trivially_copyable<From>>, int> = 0>
     [[nodiscard]] constexpr To bit_cast(const From& _Val) noexcept
     {
-#ifdef __cpp_lib_bit_cast
-#warning "Replace til::bit_cast and __builtin_bit_cast with std::bit_cast"
-#endif
+        // TODO: Replace til::bit_cast and __builtin_bit_cast with std::bit_cast
         return __builtin_bit_cast(To, _Val);
     }
 }
diff --git a/src/inc/til/enumset.h b/src/inc/til/enumset.h
index dcb5e1a330a..5ee47a71675 100644
--- a/src/inc/til/enumset.h
+++ b/src/inc/til/enumset.h
@@ -124,13 +124,14 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
         template<typename... Args>
         static constexpr UnderlyingType to_underlying(Args... positions) noexcept
         {
-            return ((UnderlyingType{ 1 } << static_cast<UnderlyingType>(positions)) | ...);
-        }
-
-        template<>
-        static constexpr UnderlyingType to_underlying() noexcept
-        {
-            return 0;
+            if constexpr (sizeof...(positions) == 0)
+            {
+                return 0;
+            }
+            else
+            {
+                return ((UnderlyingType{ 1 } << static_cast<UnderlyingType>(positions)) | ...);
+            }
         }
 
         UnderlyingType _data{};
diff --git a/src/terminal/parser/ft_fuzzer/fuzzing_directed.h b/src/terminal/parser/ft_fuzzer/fuzzing_directed.h
index aef4f3fb298..a48ed247339 100644
--- a/src/terminal/parser/ft_fuzzer/fuzzing_directed.h
+++ b/src/terminal/parser/ft_fuzzer/fuzzing_directed.h
@@ -253,25 +253,27 @@ namespace fuzz
         template<class _Type>
         static _Type GetRandom(__in _Type tMin, __in _Type tMax)
         {
-            std::mt19937 engine(m_rd()); // Mersenne twister MT19937
-            std::uniform_int_distribution<_Type> distribution(tMin, tMax);
-            auto generator = std::bind(distribution, engine);
-            return generator();
+            if constexpr (std::is_same_v<_Type, BYTE>)
+            {
+                // uniform_int_distribution only works with _Is_IntType types, which do not
+                // currently include char or unsigned char, so here is a specialization
+                // specifically for BYTE (unsigned char).
+                std::mt19937 engine(m_rd()); // Mersenne twister MT19937
+                // BYTE is unsigned, so we want to also use an unsigned type to avoid sign
+                // extension of tMin and tMax.
+                std::uniform_int_distribution<unsigned short> distribution(tMin, tMax);
+                auto generator = std::bind(distribution, engine);
+                return static_cast<BYTE>(generator());
+            }
+            else
+            {
+                std::mt19937 engine(m_rd()); // Mersenne twister MT19937
+                std::uniform_int_distribution<_Type> distribution(tMin, tMax);
+                auto generator = std::bind(distribution, engine);
+                return generator();
+            }
         }
 
-        // uniform_int_distribution only works with _Is_IntType types, which do not
-        // currently include char or unsigned char, so here is a specialization
-        // specifically for BYTE (unsigned char).
-        template<>
-        static BYTE GetRandom(__in BYTE tMin, __in BYTE tMax)
-        {
-            std::mt19937 engine(m_rd()); // Mersenne twister MT19937
-            // BYTE is unsigned, so we want to also use an unsigned type to avoid sign
-            // extension of tMin and tMax.
-            std::uniform_int_distribution<unsigned short> distribution(tMin, tMax);
-            auto generator = std::bind(distribution, engine);
-            return static_cast<BYTE>(generator());
-        }
 #ifdef __min_collision__
 #undef __min_collision__
 #define min(a, b) (((a) < (b)) ? (a) : (b))


Test patch: