Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 11 additions & 4 deletions src/cpyrt/CPPOverload.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -641,10 +641,19 @@ static PyObject* mp_vectorcall(CPPOverload* pymeth, PyObject* const* args,
pymeth->fMethodInfo->fFlags |= CallContext::kIsSorted;
}

// three stages, as the C++ ranking: exact matches, standard conversions
// (kNoImplicit: no user-defined construction from the argument), then
// implicit conversions
std::vector<Utility::PyError_t> errors;
std::vector<bool> implicit_possible(methods.size());
for (int stage = 0; stage < 2; ++stage) {
bool bHaveImplicit = false;
bool bHaveImplicit = false;
for (int stage = 0; stage < 3; ++stage) {
if (stage == 1)
ctxt.fFlags |= CallContext::kNoImplicit;
else if (stage == 2) {
ctxt.fFlags &= ~CallContext::kNoImplicit;
ctxt.fFlags |= CallContext::kAllowImplicit;
}
for (CPPOverload::Methods_t::size_type i = 0; i < nMethods; ++i) {
if (stage && !implicit_possible[i])
continue; // did not set implicit conversion, so don't try again
Expand Down Expand Up @@ -703,8 +712,6 @@ static PyObject* mp_vectorcall(CPPOverload* pymeth, PyObject* const* args,
// only move forward if implicit conversions are available
if (!bHaveImplicit)
break;

ctxt.fFlags |= CallContext::kAllowImplicit;
}

// first summarize, then add details
Expand Down
7 changes: 5 additions & 2 deletions src/cpyrt/Converters.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -353,11 +353,14 @@ static inline bool IsCPPEnumInstance(PyObject* pyobject) {
cppjit::cpyrt::PyStrings::gUnderlying);
}

// bools and enum instances match integer parameters in the implicit round only
// bools and enum instances match integer parameters in the implicit round
// only; a nested call under kNoImplicit is that round (an implicit
// construction from the argument), so enum instances pass there
static inline bool ImplicitBool(PyObject* pyobject, cpyrt::CallContext* ctxt) {
using namespace cppjit::cpyrt;
if (!AllowImplicit(ctxt) &&
(PyBool_Check(pyobject) || IsCPPEnumInstance(pyobject))) {
(PyBool_Check(pyobject) ||
(IsCPPEnumInstance(pyobject) && !NoImplicit(ctxt)))) {
if (!NoImplicit(ctxt))
ctxt->fFlags |= CallContext::kHaveImplicit;
return false;
Expand Down
26 changes: 26 additions & 0 deletions test/test_regression.py
Original file line number Diff line number Diff line change
Expand Up @@ -1686,3 +1686,29 @@ def test54_enum_arg_overload_priority(self):

# an enum instance still converts to an integer parameter
assert ns.only_uint(ns.Color.Blue) == 22

def test55_enum_arg_standard_conversion(self):
"""An enum instance converts to int before any class construction"""

import cppjit

cppjit.cppdef("""\
namespace EnumArgConversion {
enum Color { Red = 0, Green = 1, Blue = 2 };
struct FromInt { FromInt(int v) : v(v) {} int v; };
struct FromEnum { FromEnum(Color c) : v((int)c) {} int v; };
struct FromDouble { FromDouble(double d) : v((int)d) {} int v; };
int via_int(FromInt f) { return f.v; }
int via_enum(FromEnum f) { return f.v; }
int pick(int i) { return i; }
int pick(FromDouble f) { return -f.v; }
}""")

ns = cppjit.gbl.EnumArgConversion

# implicit construction from an enum instance, through int and enum
assert ns.via_int(ns.Color.Blue) == 2
assert ns.via_enum(ns.Color.Green) == 1

# a standard conversion outranks a user-defined one, as in C++
assert ns.pick(ns.Color.Blue) == 2
Loading