Error compilation of casting an enum value to the underlying type

YuryPo 25 Reputation points
2023-05-30T11:38:45.9333333+00:00

Hello

This source:

static_cast<std::underlying_type_t<T>>(value);

is compiled to the cvttsd2si instruction and hangs during debugging in latest version of MSVS 2019.

Details

This problem on godbolt: https://godbolt.org/z/jfrYv9zc8

I used MSVC of version 19.29.30148. It is from the latest version of MSVS 2019 for now. I compiled following code with options: c++20, x64, debug

#include <type_traits>
#include <format>

enum class Enum
{
    A = 1
};

template <class T> concept is_enum = std::is_enum_v<T>;

template <is_enum T>
struct std::formatter<T> : std::formatter<std::underlying_type_t<T>>
{
	constexpr auto format(T value, auto& format_context)
	{
        // Invalid generated code
		const auto undVal = static_cast<std::underlying_type_t<T>>(value);
		return formatter<std::underlying_type_t<T>>::format(undVal, format_context);
	}
};


template<typename T>
auto fn(T val)
{
    // The same cast, but all is ok
    auto v = static_cast<std::underlying_type_t<T>>(val);
    return v;
}


void main()
{
    std::format("a = {}", Enum::A);
    auto a = fn(Enum::A);
}

Assembler of casting in fn function just copy the value. But the casting in std::formatter::format uses cvttsd2si. Firstly it is incorrect for this action. Secondly my debugger hangs on this instruction.

MSVC of version 19.35 (on godbolt) compiles this code correctly.

The original problem is caused by enum formatter for fmt-library (not std). Original code was for fmt-library (not std) and can be compiled with c++17. It is also compiled correctly with my compiler (MSVC 19.29).

Migration to MSVS 2022 is imposible. Fix it in MSVS 2019 please.

Thanks in advance.

Developer technologies | C++
Developer technologies | Visual Studio | Other
{count} votes

Accepted answer
  1. Giovanni Dicanio 160 Reputation points
    2023-05-30T19:01:01.4366667+00:00

    I "massaged" a bit your original code:

        constexpr auto format(T value, auto& format_context)
    	{
            // Invalid generated code
    		const auto undVal = static_cast<std::underlying_type_t<T>>(value);
    		return formatter<std::underlying_type_t<T>>::format(undVal, format_context);
    	}
    
    

    into this form:

        template <typename FormatContext>
        constexpr auto format(T value, FormatContext& format_context) const
    	{
            // Invalid generated code
    		const auto undVal = static_cast<std::underlying_type_t<T>>(value);
    		return formatter<std::underlying_type_t<T>>::format(undVal, format_context);
    	}
    
    

    And it seems to me that the assembly code that is generated with MSVC v19.29 looks similar to what you expect, without the cvttsd2si instruction.

    Compiler Explorer link: https://godbolt.org/z/xKdM9xd8f

    Generated assembly:

            mov     eax, DWORD PTR value$[rsp]
            mov     DWORD PTR undVal$[rsp], eax
    
    
    
    

    BTW, I agree with David that, if possible, you should upgrade to the most recent version of MSVC. The quality of the MSVC C++ compiler and libraries has historically been monotonically increasing.

    1 person found this answer helpful.

0 additional answers

Sort by: Most helpful

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.