Skip to content

[BUG]: enum should not be silently converted to float in method call #5020

Open
@codeinred

Description

@codeinred

Required prerequisites

What version (or hash if on master) of pybind11 are you using?

v2.11.1

Problem description

Enums (including enum class) will be implicitly converted to ints or floats during method calls. This behavior is surprising and can lead to problems where the wrong function is selected during overload resolution.

There should be a way to disable implicit conversion of enums to other types.

Consider the following case:

PYBIND11_MODULE(cmake_example, m) {
    py::enum_<Color>(m, "Color")
        .value("RED", Color::RED)
        .value("GREEN", Color::GREEN)
        .value("BLUE", Color::BLUE);

    m.def("f", [](double, double) { return "f(double, double)"; });
    m.def("f", [](double, Color) { return "f(double, Color)"; });
}

Calling f(0, Color.RED) from python incorrectly calls the f(double, double) overload. If the Color enum were not implicitly convertible, this would not occur.

Backwards Compatibility Concerns

I understand that there may be concerns about backwards compatibility. If these concerns are significant enough, then there should at least be a way to opt out of implicit conversion for enums

Reproducible example code

Full example code (C++):

#include <pybind11/pybind11.h>

namespace py = pybind11;

enum class Color { RED = 0, GREEN = 1, BLUE = 2 };

int add_int(int i, int j) { return i + j; }
double add_float(double x, double y) { return x + y; }

PYBIND11_MODULE(cmake_example, m) {
    m.def("add_int", &add_int);
    m.def("add_float", &add_float);

    py::enum_<Color>(m, "Color")
        .value("RED", Color::RED)
        .value("GREEN", Color::GREEN)
        .value("BLUE", Color::BLUE);

    /// Example of overload failure - calling f(0, Color) in python
    /// returns "f(double, double)" due to implicit conversion
    m.def("f", [](double, double) { return "f(double, double)"; });
    m.def("f", [](double, Color) { return "f(double, Color)"; });
}

Full example code (Python):

import cmake_example as m

# Probable bug - m.Color should _not_ be implicitly convertible to int
# Both of these should throw an invalid argument exception
print(m.add_int(1, m.Color.BLUE))     # Calls add_int, prints 3
print(m.add_float(0.5, m.Color.BLUE)) # Calls add_float, prints 2.5

# Overload case
print(m.f(0.0, m.Color.BLUE)) # Calls f(double, Color) (CORRECT)
print(m.f(0, m.Color.BLUE))   # Calls f(double, double) (INCORRECT)

Output of python:

3
2.5
f(double, Color)
f(double, double)

Is this a regression? Put the last known working version here if it is.

Not a regression

Metadata

Metadata

Assignees

No one assigned

    Labels

    triageNew bug, unverified

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions