Skip to content

Commit 8af382e

Browse files
authored
Show actual runtime type if a cast fails (mypyc/mypyc#692)
Also show more expanded actual types. (An artifact of this is making the genops tests noiser but genops tests are all already unreadbly bad so whatever.) Closes mypyc/mypyc#678.
1 parent 19d7534 commit 8af382e

15 files changed

+389
-273
lines changed

mypyc/emit.py

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
Environment, BasicBlock, Value, RType, RTuple, RInstance,
1212
RUnion, RPrimitive,
1313
is_float_rprimitive, is_bool_rprimitive, is_int_rprimitive, is_short_int_rprimitive,
14-
short_name, is_list_rprimitive, is_dict_rprimitive, is_set_rprimitive, is_tuple_rprimitive,
14+
is_list_rprimitive, is_dict_rprimitive, is_set_rprimitive, is_tuple_rprimitive,
1515
is_none_rprimitive, is_object_rprimitive, object_rprimitive, is_str_rprimitive, ClassIR,
1616
FuncDecl, int_rprimitive, is_optional_type, optional_value_type, all_concrete_classes
1717
)
@@ -259,11 +259,10 @@ def emit_dec_ref(self, dest: str, rtype: RType, is_xdec: bool = False) -> None:
259259
# Otherwise assume it's an unboxed, pointerless value and do nothing.
260260

261261
def pretty_name(self, typ: RType) -> str:
262-
pretty_name = typ.name
263262
value_type = optional_value_type(typ)
264263
if value_type is not None:
265-
pretty_name = '%s or None' % self.pretty_name(value_type)
266-
return short_name(pretty_name)
264+
return '%s or None' % self.pretty_name(value_type)
265+
return str(typ)
267266

268267
def emit_cast(self, src: str, dest: str, typ: RType, declare_dest: bool = False,
269268
custom_message: Optional[str] = None, optional: bool = False,
@@ -289,8 +288,7 @@ def emit_cast(self, src: str, dest: str, typ: RType, declare_dest: bool = False,
289288
if custom_message is not None:
290289
err = custom_message
291290
else:
292-
err = 'PyErr_SetString(PyExc_TypeError, "{} object expected");'.format(
293-
self.pretty_name(typ))
291+
err = 'CPy_TypeError("{}", {});'.format(self.pretty_name(typ), src)
294292

295293
# Special case casting *from* optional
296294
if src_type and is_optional_type(src_type) and not is_object_rprimitive(typ):
@@ -498,10 +496,8 @@ def emit_unbox(self, src: str, dest: str, typ: RType, custom_failure: Optional[s
498496
declare_dest: If True, also declare the variable 'dest'
499497
borrow: If True, create a borrowed reference
500498
"""
501-
# TODO: Raise exception on failure.
502499
# TODO: Verify refcount handling.
503-
raise_exc = 'PyErr_SetString(PyExc_TypeError, "%s object expected");' % (
504-
self.pretty_name(typ))
500+
raise_exc = 'CPy_TypeError("{}", {});'.format(self.pretty_name(typ), src)
505501
if custom_failure is not None:
506502
failure = [raise_exc,
507503
custom_failure]

mypyc/lib-rt/CPy.h

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -998,6 +998,84 @@ static PyObject *CPyLong_FromFloat(PyObject *o) {
998998
}
999999
}
10001000

1001+
// Construct a nicely formatted type name based on __module__ and __name__.
1002+
static PyObject *CPy_GetTypeName(PyObject *type) {
1003+
PyObject *module = NULL, *name = NULL;
1004+
PyObject *full = NULL;
1005+
1006+
module = PyObject_GetAttrString(type, "__module__");
1007+
if (!module) {
1008+
goto out;
1009+
}
1010+
name = PyObject_GetAttrString(type, "__qualname__");
1011+
if (!name) {
1012+
goto out;
1013+
}
1014+
1015+
if (PyUnicode_CompareWithASCIIString(module, "builtins") == 0) {
1016+
Py_INCREF(name);
1017+
full = name;
1018+
} else {
1019+
full = PyUnicode_FromFormat("%U.%U", module, name);
1020+
}
1021+
1022+
out:
1023+
Py_XDECREF(module);
1024+
Py_XDECREF(name);
1025+
return full;
1026+
}
1027+
1028+
1029+
// Get the type of a value as a string, expanding tuples to include
1030+
// all the element types.
1031+
static PyObject *CPy_FormatTypeName(PyObject *value) {
1032+
if (value == Py_None) {
1033+
return PyUnicode_FromString("None");
1034+
}
1035+
1036+
if (!PyTuple_CheckExact(value)) {
1037+
return CPy_GetTypeName((PyObject *)Py_TYPE(value));
1038+
}
1039+
1040+
if (PyTuple_GET_SIZE(value) > 10) {
1041+
return PyUnicode_FromFormat("tuple[<%d items>]", PyTuple_GET_SIZE(value));
1042+
}
1043+
1044+
// Most of the logic is all for tuples, which is the only interesting case
1045+
PyObject *output = PyUnicode_FromString("tuple[");
1046+
if (!output) {
1047+
return NULL;
1048+
}
1049+
/* This is quadratic but if that ever matters something is really weird. */
1050+
for (int i = 0; i < PyTuple_GET_SIZE(value); i++) {
1051+
PyObject *s = CPy_FormatTypeName(PyTuple_GET_ITEM(value, i));
1052+
if (!s) {
1053+
Py_DECREF(output);
1054+
return NULL;
1055+
}
1056+
PyObject *next = PyUnicode_FromFormat("%U%U%s", output, s,
1057+
i + 1 == PyTuple_GET_SIZE(value) ? "]" : ", ");
1058+
Py_DECREF(output);
1059+
Py_DECREF(s);
1060+
if (!next) {
1061+
return NULL;
1062+
}
1063+
output = next;
1064+
}
1065+
return output;
1066+
}
1067+
1068+
static void CPy_TypeError(const char *expected, PyObject *value) {
1069+
PyObject *out = CPy_FormatTypeName(value);
1070+
if (out) {
1071+
PyErr_Format(PyExc_TypeError, "%s object expected; got %U", expected, out);
1072+
Py_DECREF(out);
1073+
} else {
1074+
PyErr_Format(PyExc_TypeError, "%s object expected; and errored formatting real type!",
1075+
expected);
1076+
}
1077+
}
1078+
10011079
// These functions are basically exactly PyCode_NewEmpty and
10021080
// _PyTraceback_Add which are available in all the versions we support.
10031081
// We're continuing to use them because we'll probably optimize them later.

mypyc/ops.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -277,7 +277,9 @@ class RInstance(RType):
277277
is_unboxed = False
278278

279279
def __init__(self, class_ir: 'ClassIR') -> None:
280-
self.name = class_ir.name
280+
# name is used for formatting the name in messages and debug output
281+
# so we want the fullname for precision.
282+
self.name = class_ir.fullname
281283
self.class_ir = class_ir
282284
self._ctype = 'PyObject *'
283285

@@ -1591,6 +1593,10 @@ def __init__(self, name: str, module_name: str, is_trait: bool = False,
15911593
# in a few ad-hoc cases.
15921594
self.builtin_base = None # type: Optional[str]
15931595

1596+
@property
1597+
def fullname(self) -> str:
1598+
return "{}.{}".format(self.module_name, self.name)
1599+
15941600
def real_base(self) -> Optional['ClassIR']:
15951601
"""Return the actual concrete base class, if there is one."""
15961602
if len(self.mro) > 1 and not self.mro[1].is_trait:

mypyc/test/test_emitfunc.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ def test_unbox(self) -> None:
172172
"""if (likely(PyLong_Check(cpy_r_m)))
173173
cpy_r_r0 = CPyTagged_FromObject(cpy_r_m);
174174
else {
175-
PyErr_SetString(PyExc_TypeError, "int object expected");
175+
CPy_TypeError("int", cpy_r_m);
176176
cpy_r_r0 = CPY_INT_TAG;
177177
}
178178
""")

mypyc/test/test_emitwrapper.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ def test_check_list(self) -> None:
2121
'if (likely(PyList_Check(obj_x)))',
2222
' arg_x = obj_x;',
2323
'else {',
24-
' PyErr_SetString(PyExc_TypeError, "list object expected");',
24+
' CPy_TypeError("list", obj_x);',
2525
' arg_x = NULL;',
2626
'}',
2727
'if (arg_x == NULL) return NULL;',
@@ -37,7 +37,7 @@ def test_check_int(self) -> None:
3737
'if (likely(PyLong_Check(obj_x)))',
3838
' arg_x = CPyTagged_BorrowFromObject(obj_x);',
3939
'else {',
40-
' PyErr_SetString(PyExc_TypeError, "int object expected");',
40+
' CPy_TypeError("int", obj_x);',
4141
' return NULL;',
4242
'}',
4343
'CPyTagged arg_y;',
@@ -46,7 +46,7 @@ def test_check_int(self) -> None:
4646
'} else if (likely(PyLong_Check(obj_y)))',
4747
' arg_y = CPyTagged_BorrowFromObject(obj_y);',
4848
'else {',
49-
' PyErr_SetString(PyExc_TypeError, "int object expected");',
49+
' CPy_TypeError("int", obj_y);',
5050
' return NULL;',
5151
'}',
5252
], lines)

test-data/exceptions.test

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -73,12 +73,12 @@ def f(x: Optional[A]) -> int:
7373
return 3
7474
[out]
7575
def f(x):
76-
x :: union[A, None]
76+
x :: union[__main__.A, None]
7777
r0 :: None
7878
r1 :: object
7979
r2 :: bool
8080
r3 :: short_int
81-
r4 :: A
81+
r4 :: __main__.A
8282
r5 :: None
8383
r6 :: object
8484
r7, r8 :: bool
@@ -94,7 +94,7 @@ L1:
9494
return r3
9595
L2:
9696
inc_ref x
97-
r4 = cast(A, x)
97+
r4 = cast(__main__.A, x)
9898
if is_error(r4) goto L6 (error at f:8) else goto L3
9999
L3:
100100
r5 = None

test-data/genops-any.test

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ def f(a: Any, n: int, c: C) -> None:
4242
def f(a, n, c):
4343
a :: object
4444
n :: int
45-
c :: C
45+
c :: __main__.C
4646
r0 :: object
4747
r1 :: bool
4848
r2 :: int

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy