diff --git a/Doc/library/token-list.inc b/Doc/library/token-list.inc index 877d39a432ed70..d3c3a2b8b69e82 100644 --- a/Doc/library/token-list.inc +++ b/Doc/library/token-list.inc @@ -201,6 +201,10 @@ Token value for ``":="``. +.. data:: QUESTION + + Token value for ``"?"``. + .. data:: OP .. data:: AWAIT diff --git a/Grammar/Tokens b/Grammar/Tokens index 9de2da5d15fc3b..dbfcd310de86d3 100644 --- a/Grammar/Tokens +++ b/Grammar/Tokens @@ -53,6 +53,7 @@ ATEQUAL '@=' RARROW '->' ELLIPSIS '...' COLONEQUAL ':=' +QUESTION '?' OP AWAIT diff --git a/Grammar/python.gram b/Grammar/python.gram index c5a5dbe1724f3e..43a0b1e474b31f 100644 --- a/Grammar/python.gram +++ b/Grammar/python.gram @@ -327,6 +327,7 @@ expressions[expr_ty]: _Py_Tuple(CHECK(_PyPegen_seq_insert_in_front(p, a, b)), Load, EXTRA) } | a=expression ',' { _Py_Tuple(CHECK(_PyPegen_singleton_seq(p, a)), Load, EXTRA) } | expression + expression[expr_ty] (memo): | a=disjunction 'if' b=disjunction 'else' c=expression { _Py_IfExp(b, a, c, EXTRA) } | disjunction @@ -452,6 +453,7 @@ factor[expr_ty] (memo): | '+' a=factor { _Py_UnaryOp(UAdd, a, EXTRA) } | '-' a=factor { _Py_UnaryOp(USub, a, EXTRA) } | '~' a=factor { _Py_UnaryOp(Invert, a, EXTRA) } + | a=factor '?' { _Py_UnaryOp(Question, a, EXTRA) } | power power[expr_ty]: | a=await_primary '**' b=factor { _Py_BinOp(a, Pow, b, EXTRA) } @@ -656,8 +658,8 @@ invalid_assignment: RAISE_SYNTAX_ERROR_INVALID_TARGET(STAR_TARGETS, a) } | (star_targets '=')* a=yield_expr '=' { RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "assignment to yield expression not possible") } | a=star_expressions augassign (yield_expr | star_expressions) { - RAISE_SYNTAX_ERROR_KNOWN_LOCATION( - a, + RAISE_SYNTAX_ERROR_KNOWN_LOCATION( + a, "'%s' is an illegal expression for augmented assignment", _PyPegen_get_expr_name(a) )} diff --git a/Include/Python-ast.h b/Include/Python-ast.h index e7afa1e6579e8d..258f79ef166de7 100644 --- a/Include/Python-ast.h +++ b/Include/Python-ast.h @@ -25,7 +25,8 @@ typedef enum _operator { Add=1, Sub=2, Mult=3, MatMult=4, Div=5, Mod=6, Pow=7, LShift=8, RShift=9, BitOr=10, BitXor=11, BitAnd=12, FloorDiv=13 } operator_ty; -typedef enum _unaryop { Invert=1, Not=2, UAdd=3, USub=4 } unaryop_ty; +typedef enum _unaryop { Invert=1, Not=2, UAdd=3, USub=4, Question=5 } + unaryop_ty; typedef enum _cmpop { Eq=1, NotEq=2, Lt=3, LtE=4, Gt=5, GtE=6, Is=7, IsNot=8, In=9, NotIn=10 } cmpop_ty; @@ -223,7 +224,7 @@ struct _stmt { int end_col_offset; }; -enum _expr_kind {BoolOp_kind=1, NamedExpr_kind=2, BinOp_kind=3, UnaryOp_kind=4, +enum _expr_kind {BoolOp_kind=1, NamedExpr_kind=2, UnaryOp_kind=3, BinOp_kind=4, Lambda_kind=5, IfExp_kind=6, Dict_kind=7, Set_kind=8, ListComp_kind=9, SetComp_kind=10, DictComp_kind=11, GeneratorExp_kind=12, Await_kind=13, Yield_kind=14, @@ -244,17 +245,17 @@ struct _expr { expr_ty value; } NamedExpr; + struct { + unaryop_ty op; + expr_ty operand; + } UnaryOp; + struct { expr_ty left; operator_ty op; expr_ty right; } BinOp; - struct { - unaryop_ty op; - expr_ty operand; - } UnaryOp; - struct { arguments_ty args; expr_ty body; @@ -565,13 +566,13 @@ expr_ty _Py_BoolOp(boolop_ty op, asdl_seq * values, int lineno, int col_offset, expr_ty _Py_NamedExpr(expr_ty target, expr_ty value, int lineno, int col_offset, int end_lineno, int end_col_offset, PyArena *arena); +#define UnaryOp(a0, a1, a2, a3, a4, a5, a6) _Py_UnaryOp(a0, a1, a2, a3, a4, a5, a6) +expr_ty _Py_UnaryOp(unaryop_ty op, expr_ty operand, int lineno, int col_offset, + int end_lineno, int end_col_offset, PyArena *arena); #define BinOp(a0, a1, a2, a3, a4, a5, a6, a7) _Py_BinOp(a0, a1, a2, a3, a4, a5, a6, a7) expr_ty _Py_BinOp(expr_ty left, operator_ty op, expr_ty right, int lineno, int col_offset, int end_lineno, int end_col_offset, PyArena *arena); -#define UnaryOp(a0, a1, a2, a3, a4, a5, a6) _Py_UnaryOp(a0, a1, a2, a3, a4, a5, a6) -expr_ty _Py_UnaryOp(unaryop_ty op, expr_ty operand, int lineno, int col_offset, - int end_lineno, int end_col_offset, PyArena *arena); #define Lambda(a0, a1, a2, a3, a4, a5, a6) _Py_Lambda(a0, a1, a2, a3, a4, a5, a6) expr_ty _Py_Lambda(arguments_ty args, expr_ty body, int lineno, int col_offset, int end_lineno, int end_col_offset, PyArena *arena); diff --git a/Include/Python.h b/Include/Python.h index 57f71d41d8d477..b9f3cee218e2fd 100644 --- a/Include/Python.h +++ b/Include/Python.h @@ -122,6 +122,7 @@ #include "iterobject.h" #include "genobject.h" #include "descrobject.h" +#include "optionalobject.h" #include "genericaliasobject.h" #include "warnings.h" #include "weakrefobject.h" diff --git a/Include/opcode.h b/Include/opcode.h index 19944fac0b9f2b..300dbdc6385a96 100644 --- a/Include/opcode.h +++ b/Include/opcode.h @@ -17,6 +17,7 @@ extern "C" { #define UNARY_POSITIVE 10 #define UNARY_NEGATIVE 11 #define UNARY_NOT 12 +#define UNARY_QUESTION 14 #define UNARY_INVERT 15 #define BINARY_MATRIX_MULTIPLY 16 #define INPLACE_MATRIX_MULTIPLY 17 diff --git a/Include/optionalobject.h b/Include/optionalobject.h new file mode 100644 index 00000000000000..ffeeafcb234efb --- /dev/null +++ b/Include/optionalobject.h @@ -0,0 +1,15 @@ + +#ifndef Py_OPTIONALTYPEOBJECT_H +#define Py_OPTIONALTYPEOBJECT_H +#ifdef __cplusplus +extern "C" +{ +#endif + + PyAPI_FUNC(PyObject *) Py_Optional(PyObject *); + PyAPI_DATA(PyTypeObject) Py_OptionalType; + +#ifdef __cplusplus +} +#endif +#endif /* !Py_OPTIONALTYPEOBJECT_H */ diff --git a/Include/token.h b/Include/token.h index 9b8a3aae074674..c4f30f75ca7ffb 100644 --- a/Include/token.h +++ b/Include/token.h @@ -64,13 +64,14 @@ extern "C" { #define RARROW 51 #define ELLIPSIS 52 #define COLONEQUAL 53 -#define OP 54 -#define AWAIT 55 -#define ASYNC 56 -#define TYPE_IGNORE 57 -#define TYPE_COMMENT 58 -#define ERRORTOKEN 59 -#define N_TOKENS 63 +#define QUESTION 54 +#define OP 55 +#define AWAIT 56 +#define ASYNC 57 +#define TYPE_IGNORE 58 +#define TYPE_COMMENT 59 +#define ERRORTOKEN 60 +#define N_TOKENS 64 #define NT_OFFSET 256 /* Special definitions for cooperation with parser */ diff --git a/Lib/opcode.py b/Lib/opcode.py index ac1aa535f66ff6..f0e88619c0794d 100644 --- a/Lib/opcode.py +++ b/Lib/opcode.py @@ -65,7 +65,7 @@ def jabs_op(name, op): def_op('UNARY_POSITIVE', 10) def_op('UNARY_NEGATIVE', 11) def_op('UNARY_NOT', 12) - +def_op('UNARY_QUESTION', 14) def_op('UNARY_INVERT', 15) def_op('BINARY_MATRIX_MULTIPLY', 16) diff --git a/Lib/test/test_grammar.py b/Lib/test/test_grammar.py index ef7d1a15c7570d..99a0ab590ca794 100644 --- a/Lib/test/test_grammar.py +++ b/Lib/test/test_grammar.py @@ -407,6 +407,10 @@ def test_var_annot_simple_exec(self): with self.assertRaises(KeyError): gns['__annotations__'] + def test_annot_optional(self): + x: int? = None + self.assertRaises(SyntaxError, eval, "?bar = 1") + def test_var_annot_custom_maps(self): # tests with custom locals() and __annotations__ ns = {'__annotations__': CNS()} diff --git a/Lib/test/test_isinstance.py b/Lib/test/test_isinstance.py index 53639e984e48a7..af5043c32faced 100644 --- a/Lib/test/test_isinstance.py +++ b/Lib/test/test_isinstance.py @@ -4,6 +4,7 @@ import unittest import sys +import typing @@ -271,6 +272,13 @@ def __bases__(self): self.assertEqual(True, issubclass(B(), int)) + def test_optional_isinstance(self): + self.assertTrue(issubclass(int, int?)) + self.assertTrue(issubclass(Child, Super?)) + self.assertTrue(isinstance(Child(), Super?)) + self.assertTrue(isinstance(None, Super?)) + + def blowstack(fxn, arg, compare_to): # Make sure that calling isinstance with a deeply nested tuple for its diff --git a/Lib/test/test_types.py b/Lib/test/test_types.py index 49dc5bf40e3ed8..6a263574d938a0 100644 --- a/Lib/test/test_types.py +++ b/Lib/test/test_types.py @@ -7,6 +7,7 @@ import locale import sys import types +import typing import unittest.mock import weakref @@ -598,6 +599,9 @@ def test_method_descriptor_types(self): self.assertIsInstance(int.from_bytes, types.BuiltinMethodType) self.assertIsInstance(int.__new__, types.BuiltinMethodType) + def test_optional_operator_types(self): + self.assertEqual(int?, typing.Optional[int]) + self.assertIsInstance(1, int?) class MappingProxyTests(unittest.TestCase): mappingproxy = types.MappingProxyType @@ -803,7 +807,6 @@ def test_union(self): self.assertDictEqual(mapping, {'a': 0, 'b': 1, 'c': 2}) self.assertDictEqual(other, {'c': 3, 'p': 0}) - class ClassCreationTests(unittest.TestCase): class Meta(type): diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index f429e883b59538..423f26e7bf5fe3 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -3585,6 +3585,17 @@ def foo(a: A) -> Optional[BaseException]: assert isinstance(foo(KeyboardInterrupt), KeyboardInterrupt) assert foo(None) is None + def test_new_optional(self): + assert int? == Optional[int] + assert int? != int + + A = Type[BaseException]? + def foo(a: A) -> BaseException?: + if a is None: + return None + else: + return a() + assert isinstance(foo(KeyboardInterrupt), KeyboardInterrupt) class NewTypeTests(BaseTestCase): diff --git a/Lib/token.py b/Lib/token.py index 493bf0426508fe..19de47c3cb8183 100644 --- a/Lib/token.py +++ b/Lib/token.py @@ -57,17 +57,18 @@ RARROW = 51 ELLIPSIS = 52 COLONEQUAL = 53 -OP = 54 -AWAIT = 55 -ASYNC = 56 -TYPE_IGNORE = 57 -TYPE_COMMENT = 58 +QUESTION = 54 +OP = 55 +AWAIT = 56 +ASYNC = 57 +TYPE_IGNORE = 58 +TYPE_COMMENT = 59 # These aren't used by the C tokenizer but are needed for tokenize.py -ERRORTOKEN = 59 -COMMENT = 60 -NL = 61 -ENCODING = 62 -N_TOKENS = 63 +ERRORTOKEN = 60 +COMMENT = 61 +NL = 62 +ENCODING = 63 +N_TOKENS = 64 # Special definitions for cooperation with parser NT_OFFSET = 256 @@ -113,6 +114,7 @@ '>=': GREATEREQUAL, '>>': RIGHTSHIFT, '>>=': RIGHTSHIFTEQUAL, + '?': QUESTION, '@': AT, '@=': ATEQUAL, '[': LSQB, diff --git a/Lib/tokenize.py b/Lib/tokenize.py index 1aee21b5e18fa7..306d8821c981dd 100644 --- a/Lib/tokenize.py +++ b/Lib/tokenize.py @@ -600,7 +600,6 @@ def _tokenize(readline, encoding): yield TokenInfo(ERRORTOKEN, line[pos], (lnum, pos), (lnum, pos+1), line) pos += 1 - # Add an implicit NEWLINE if the input doesn't end in one if last_line and last_line[-1] not in '\r\n': yield TokenInfo(NEWLINE, '', (lnum - 1, len(last_line)), (lnum - 1, len(last_line) + 1), '') diff --git a/Makefile.pre.in b/Makefile.pre.in index 3428b9842a5a0f..23b2eda28a61ef 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -429,6 +429,7 @@ OBJECT_OBJS= \ Objects/typeobject.o \ Objects/unicodeobject.o \ Objects/unicodectype.o \ + Objects/optionalobject.o \ Objects/weakrefobject.o ########################################################################## diff --git a/Objects/optionalobject.c b/Objects/optionalobject.c new file mode 100644 index 00000000000000..ac62152cc884b2 --- /dev/null +++ b/Objects/optionalobject.c @@ -0,0 +1,283 @@ +// types.Optional -- used to represent typing.Optional[int] as ?int +#include "Python.h" +#include "pycore_object.h" +#include "structmember.h" + + +typedef struct { + PyObject_HEAD + PyObject *args; +} optionalobject; + +static void +optionalobject_dealloc(PyObject *self) +{ + optionalobject *alias = (optionalobject *)self; + + Py_XDECREF(alias->args); + self->ob_type->tp_free(self); +} + +static Py_hash_t +optional_hash(PyObject *self) +{ + optionalobject *alias = (optionalobject *)self; + Py_hash_t h1 = PyObject_Hash(alias->args); + if (h1 == -1) { + return -1; + } + return h1; +} + +static PyMemberDef optional_members[] = { + {"__args__", T_OBJECT, offsetof(optionalobject, args), READONLY}, + {0} +}; + +static PyObject * +optional_getattro(PyObject *self, PyObject *name) +{ + return PyObject_GenericGetAttr(self, name); +} + +static PyObject * +optional_instancecheck(PyObject *self, PyObject *instance) +{ + optionalobject *alias = (optionalobject *)self; + PyObject *arg = alias->args; + if (PyType_Check(arg) && PyObject_IsInstance(instance, arg) != 0) { + Py_RETURN_TRUE; + } + if (instance == Py_None) { + Py_RETURN_TRUE; + } + Py_RETURN_FALSE; +} + +static PyObject * +optional_subclasscheck(PyObject *self, PyObject *instance) +{ + if (!PyType_Check(instance)) { + PyErr_SetString(PyExc_TypeError, "issubclass() arg 1 must be a class"); + return NULL; + } + + optionalobject *alias = (optionalobject *)self; + PyObject *arg = alias->args; + + if (PyType_Check(arg) && (PyType_IsSubtype((PyTypeObject *)instance, (PyTypeObject *)arg) != 0)) { + Py_RETURN_TRUE; + } + + Py_RETURN_FALSE; +} + +static PyMethodDef optional_methods[] = { + {"__instancecheck__", optional_instancecheck, METH_O}, + {"__subclasscheck__", optional_subclasscheck, METH_O}, + {0}}; + +static int +is_typing_module(PyObject *obj) { + PyObject *module = PyObject_GetAttrString(obj, "__module__"); + if (module == NULL) { + return -1; + } + int is_typing = PyUnicode_Check(module) && _PyUnicode_EqualToASCIIString(module, "typing"); + Py_DECREF(module); + return is_typing; +} + +static int +is_typing_name(PyObject *obj, char *name) +{ + PyTypeObject *type = Py_TYPE(obj); + if (strcmp(type->tp_name, name) != 0) { + return 0; + } + return is_typing_module(obj); +} + +static PyObject * +optional_richcompare(PyObject *a, PyObject *b, int op) +{ + if (op != Py_EQ && op != Py_NE) { + PyObject *result = Py_NotImplemented; + Py_INCREF(result); + return result; + } + optionalobject *aa = (optionalobject *)a; + + int is_typing_union = is_typing_name(b, "_UnionGenericAlias"); + if (is_typing_union < 0) { + return NULL; + } + + PyTypeObject *type = Py_TYPE(b); + if (is_typing_union) { + // create a tuple of the optional arg + PyNone. + PyObject *a_tuple = PyTuple_Pack(2, aa->args, Py_TYPE(Py_None)); + if (a_tuple == NULL) { + return NULL; + } + PyObject *a_set = PySet_New(a_tuple); + if (a_set == NULL) { + Py_DECREF(a_set); + return NULL; + } + Py_DECREF(a_tuple); + + PyObject* b_args = PyObject_GetAttrString(b, "__args__"); + if (b_args == NULL) { + Py_DECREF(a_set); + return NULL; + } + PyObject *b_set = PySet_New(b_args); + if (b_set == NULL) { + Py_DECREF(a_set); + Py_DECREF(b_args); + return NULL; + } + Py_DECREF(b_args); + PyObject *result = PyObject_RichCompare(a_set, b_set, op); + Py_DECREF(a_set); + Py_DECREF(b_set); + return result; + } else if (type == &Py_OptionalType) { + optionalobject *bb = (optionalobject *)b; + PyObject *result = PyObject_RichCompare(aa->args, bb->args, op); + return result; + } + if (op == Py_NE) { + Py_RETURN_TRUE; + } + Py_RETURN_FALSE; +} + + +static int +optional_repr_item(_PyUnicodeWriter *writer, PyObject *p) +{ + _Py_IDENTIFIER(__module__); + _Py_IDENTIFIER(__qualname__); + _Py_IDENTIFIER(__origin__); + _Py_IDENTIFIER(__args__); + PyObject *qualname = NULL; + PyObject *module = NULL; + PyObject *r = NULL; + PyObject *tmp; + int err; + + if (p == Py_Ellipsis) { + // The Ellipsis object + r = PyUnicode_FromString("..."); + goto done; + } + + if (_PyObject_LookupAttrId(p, &PyId___origin__, &tmp) < 0) { + goto done; + } + if (tmp != NULL) { + Py_DECREF(tmp); + if (_PyObject_LookupAttrId(p, &PyId___args__, &tmp) < 0) { + goto done; + } + if (tmp != NULL) { + Py_DECREF(tmp); + // It looks like a GenericAlias + goto use_repr; + } + } + + if (_PyObject_LookupAttrId(p, &PyId___qualname__, &qualname) < 0) { + goto done; + } + if (qualname == NULL) { + goto use_repr; + } + if (_PyObject_LookupAttrId(p, &PyId___module__, &module) < 0) { + goto done; + } + if (module == NULL || module == Py_None) { + goto use_repr; + } + + // Looks like a class + if (PyUnicode_Check(module) && + _PyUnicode_EqualToASCIIString(module, "builtins")) + { + // builtins don't need a module name + r = PyObject_Str(qualname); + goto done; + } + else { + r = PyUnicode_FromFormat("%S.%S", module, qualname); + goto done; + } + +use_repr: + r = PyObject_Repr(p); + +done: + Py_XDECREF(qualname); + Py_XDECREF(module); + if (r == NULL) { + // error if any of the above PyObject_Repr/PyUnicode_From* fail + err = -1; + } + else { + err = _PyUnicodeWriter_WriteStr(writer, r); + Py_DECREF(r); + } + return err; +} + +static PyObject * +optional_repr(PyObject *self) +{ + optionalobject *alias = (optionalobject *)self; + _PyUnicodeWriter writer; + _PyUnicodeWriter_Init(&writer); + if (_PyUnicodeWriter_WriteASCIIString(&writer, "?", 1) < 0) { + goto error; + } + if (optional_repr_item(&writer, alias->args) < 0) { + goto error; + } + return _PyUnicodeWriter_Finish(&writer); + +error: + _PyUnicodeWriter_Dealloc(&writer); + return NULL; +} + +PyTypeObject Py_OptionalType = { + PyVarObject_HEAD_INIT(&PyType_Type, 0) + .tp_name = "types.Optional", + .tp_doc = "Represent an Optional type\n" + "\n" + "E.g. ?int", + .tp_basicsize = sizeof(optionalobject), + .tp_dealloc = optionalobject_dealloc, + .tp_alloc = PyType_GenericAlloc, + .tp_free = PyObject_Del, + .tp_flags = Py_TPFLAGS_DEFAULT, + .tp_hash = optional_hash, + .tp_getattro = optional_getattro, + .tp_members = optional_members, + .tp_methods = optional_methods, + .tp_richcompare = optional_richcompare, + .tp_repr = optional_repr, +}; + +PyObject * +Py_Optional(PyObject *args) +{ + optionalobject *alias = PyObject_New(optionalobject, &Py_OptionalType); + if (alias == NULL) { + return NULL; + } + Py_INCREF(args); + alias->args = args; + return (PyObject *) alias; +} diff --git a/PCbuild/pythoncore.vcxproj b/PCbuild/pythoncore.vcxproj index db26e38911bc05..1282b1e2f7bc2e 100644 --- a/PCbuild/pythoncore.vcxproj +++ b/PCbuild/pythoncore.vcxproj @@ -391,6 +391,7 @@ + diff --git a/PCbuild/pythoncore.vcxproj.filters b/PCbuild/pythoncore.vcxproj.filters index 1f3883768c9a25..64cf6113f273dc 100644 --- a/PCbuild/pythoncore.vcxproj.filters +++ b/PCbuild/pythoncore.vcxproj.filters @@ -1178,10 +1178,13 @@ Objects + + Objects + Resource Files - \ No newline at end of file + diff --git a/Parser/Python.asdl b/Parser/Python.asdl index 889712b4b3d36e..4ff475e344f058 100644 --- a/Parser/Python.asdl +++ b/Parser/Python.asdl @@ -54,8 +54,8 @@ module Python -- BoolOp() can use left & right? expr = BoolOp(boolop op, expr* values) | NamedExpr(expr target, expr value) - | BinOp(expr left, operator op, expr right) | UnaryOp(unaryop op, expr operand) + | BinOp(expr left, operator op, expr right) | Lambda(arguments args, expr body) | IfExp(expr test, expr body, expr orelse) | Dict(expr* keys, expr* values) @@ -97,7 +97,7 @@ module Python operator = Add | Sub | Mult | MatMult | Div | Mod | Pow | LShift | RShift | BitOr | BitXor | BitAnd | FloorDiv - unaryop = Invert | Not | UAdd | USub + unaryop = Invert | Not | UAdd | USub | Question cmpop = Eq | NotEq | Lt | LtE | Gt | GtE | Is | IsNot | In | NotIn diff --git a/Parser/parser.c b/Parser/parser.c index 323cd0e0efae30..05ed85d8077dfa 100644 --- a/Parser/parser.c +++ b/Parser/parser.c @@ -166,7 +166,7 @@ static KeywordToken *reserved_keywords[] = { #define shift_expr_type 1097 // Left-recursive #define sum_type 1098 // Left-recursive #define term_type 1099 // Left-recursive -#define factor_type 1100 +#define factor_type 1100 // Left-recursive #define power_type 1101 #define await_primary_type 1102 #define primary_type 1103 // Left-recursive @@ -6861,7 +6861,11 @@ annotated_rhs_rule(Parser *p) return _res; } -// expressions: expression ((',' expression))+ ','? | expression ',' | expression +// expressions: +// | expression ((',' expression))+ ','? +// | expression ',' +// | expression '?' +// | expression static expr_ty expressions_rule(Parser *p) { @@ -6957,6 +6961,42 @@ expressions_rule(Parser *p) D(fprintf(stderr, "%*c%s expressions[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "expression ','")); } + { // expression '?' + if (p->error_indicator) { + D(p->level--); + return NULL; + } + D(fprintf(stderr, "%*c> expressions[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "expression '?'")); + expr_ty a; + Token * b; + if ( + (a = expression_rule(p)) // expression + && + (b = _PyPegen_expect_token(p, 54)) // token='?' + ) + { + D(fprintf(stderr, "%*c+ expressions[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression '?'")); + Token *_token = _PyPegen_get_last_nonnwhitespace_token(p); + if (_token == NULL) { + D(p->level--); + return NULL; + } + int _end_lineno = _token->end_lineno; + UNUSED(_end_lineno); // Only used by EXTRA macro + int _end_col_offset = _token->end_col_offset; + UNUSED(_end_col_offset); // Only used by EXTRA macro + _res = _Py_UnaryOp ( Question , a , EXTRA ); + if (_res == NULL && PyErr_Occurred()) { + p->error_indicator = 1; + D(p->level--); + return NULL; + } + goto done; + } + p->mark = _mark; + D(fprintf(stderr, "%*c%s expressions[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "expression '?'")); + } { // expression if (p->error_indicator) { D(p->level--); @@ -9928,21 +9968,47 @@ term_raw(Parser *p) return _res; } -// factor: '+' factor | '-' factor | '~' factor | power +// Left-recursive +// factor: '+' factor | '-' factor | '~' factor | factor '?' | power +static expr_ty factor_raw(Parser *); static expr_ty factor_rule(Parser *p) { D(p->level++); - if (p->error_indicator) { - D(p->level--); - return NULL; - } expr_ty _res = NULL; if (_PyPegen_is_memoized(p, factor_type, &_res)) { D(p->level--); return _res; } int _mark = p->mark; + int _resmark = p->mark; + while (1) { + int tmpvar_7 = _PyPegen_update_memo(p, _mark, factor_type, _res); + if (tmpvar_7) { + D(p->level--); + return _res; + } + p->mark = _mark; + void *_raw = factor_raw(p); + if (_raw == NULL || p->mark <= _resmark) + break; + _resmark = p->mark; + _res = _raw; + } + p->mark = _resmark; + D(p->level--); + return _res; +} +static expr_ty +factor_raw(Parser *p) +{ + D(p->level++); + if (p->error_indicator) { + D(p->level--); + return NULL; + } + expr_ty _res = NULL; + int _mark = p->mark; if (p->mark == p->fill && _PyPegen_fill_token(p) < 0) { p->error_indicator = 1; D(p->level--); @@ -10060,6 +10126,42 @@ factor_rule(Parser *p) D(fprintf(stderr, "%*c%s factor[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'~' factor")); } + { // factor '?' + if (p->error_indicator) { + D(p->level--); + return NULL; + } + D(fprintf(stderr, "%*c> factor[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "factor '?'")); + Token * _literal; + expr_ty a; + if ( + (a = factor_rule(p)) // factor + && + (_literal = _PyPegen_expect_token(p, 54)) // token='?' + ) + { + D(fprintf(stderr, "%*c+ factor[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "factor '?'")); + Token *_token = _PyPegen_get_last_nonnwhitespace_token(p); + if (_token == NULL) { + D(p->level--); + return NULL; + } + int _end_lineno = _token->end_lineno; + UNUSED(_end_lineno); // Only used by EXTRA macro + int _end_col_offset = _token->end_col_offset; + UNUSED(_end_col_offset); // Only used by EXTRA macro + _res = _Py_UnaryOp ( Question , a , EXTRA ); + if (_res == NULL && PyErr_Occurred()) { + p->error_indicator = 1; + D(p->level--); + return NULL; + } + goto done; + } + p->mark = _mark; + D(fprintf(stderr, "%*c%s factor[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "factor '?'")); + } { // power if (p->error_indicator) { D(p->level--); @@ -10081,7 +10183,6 @@ factor_rule(Parser *p) } _res = NULL; done: - _PyPegen_insert_memo(p, _mark, factor_type, _res); D(p->level--); return _res; } @@ -10276,8 +10377,8 @@ primary_rule(Parser *p) int _mark = p->mark; int _resmark = p->mark; while (1) { - int tmpvar_7 = _PyPegen_update_memo(p, _mark, primary_type, _res); - if (tmpvar_7) { + int tmpvar_8 = _PyPegen_update_memo(p, _mark, primary_type, _res); + if (tmpvar_8) { D(p->level--); return _res; } @@ -13928,8 +14029,8 @@ t_primary_rule(Parser *p) int _mark = p->mark; int _resmark = p->mark; while (1) { - int tmpvar_8 = _PyPegen_update_memo(p, _mark, t_primary_type, _res); - if (tmpvar_8) { + int tmpvar_9 = _PyPegen_update_memo(p, _mark, t_primary_type, _res); + if (tmpvar_9) { D(p->level--); return _res; } diff --git a/Parser/token.c b/Parser/token.c index a489668900901d..1541f17a89dc82 100644 --- a/Parser/token.c +++ b/Parser/token.c @@ -60,6 +60,7 @@ const char * const _PyParser_TokenNames[] = { "RARROW", "ELLIPSIS", "COLONEQUAL", + "QUESTION", "OP", "AWAIT", "ASYNC", @@ -93,6 +94,7 @@ PyToken_OneChar(int c1) case '<': return LESS; case '=': return EQUAL; case '>': return GREATER; + case '?': return QUESTION; case '@': return AT; case '[': return LSQB; case ']': return RSQB; diff --git a/Parser/tokenizer.c b/Parser/tokenizer.c index f3c1d9b20ade11..6c4295b8d00c8f 100644 --- a/Parser/tokenizer.c +++ b/Parser/tokenizer.c @@ -1378,6 +1378,11 @@ tok_get(struct tok_state *tok, const char **p_start, const char **p_end) return tok->done == E_EOF ? ENDMARKER : ERRORTOKEN; } + /* Check for Optional shorthand */ + if (c == '?') { + return QUESTION; + } + /* Identifier (most frequent token!) */ nonascii = 0; if (is_potential_identifier_start(c)) { diff --git a/Python/Python-ast.c b/Python/Python-ast.c index 694987dd077881..f29c1dda36b6bb 100644 --- a/Python/Python-ast.c +++ b/Python/Python-ast.c @@ -106,6 +106,8 @@ typedef struct { PyObject *Pass_type; PyObject *Pow_singleton; PyObject *Pow_type; + PyObject *Question_singleton; + PyObject *Question_type; PyObject *RShift_singleton; PyObject *RShift_type; PyObject *Raise_type; @@ -326,6 +328,8 @@ static int astmodule_clear(PyObject *module) Py_CLEAR(astmodulestate(module)->Pass_type); Py_CLEAR(astmodulestate(module)->Pow_singleton); Py_CLEAR(astmodulestate(module)->Pow_type); + Py_CLEAR(astmodulestate(module)->Question_singleton); + Py_CLEAR(astmodulestate(module)->Question_type); Py_CLEAR(astmodulestate(module)->RShift_singleton); Py_CLEAR(astmodulestate(module)->RShift_type); Py_CLEAR(astmodulestate(module)->Raise_type); @@ -545,6 +549,8 @@ static int astmodule_traverse(PyObject *module, visitproc visit, void* arg) Py_VISIT(astmodulestate(module)->Pass_type); Py_VISIT(astmodulestate(module)->Pow_singleton); Py_VISIT(astmodulestate(module)->Pow_type); + Py_VISIT(astmodulestate(module)->Question_singleton); + Py_VISIT(astmodulestate(module)->Question_type); Py_VISIT(astmodulestate(module)->RShift_singleton); Py_VISIT(astmodulestate(module)->RShift_type); Py_VISIT(astmodulestate(module)->Raise_type); @@ -909,15 +915,15 @@ static const char * const NamedExpr_fields[]={ "target", "value", }; +static const char * const UnaryOp_fields[]={ + "op", + "operand", +}; static const char * const BinOp_fields[]={ "left", "op", "right", }; -static const char * const UnaryOp_fields[]={ - "op", - "operand", -}; static const char * const Lambda_fields[]={ "args", "body", @@ -1597,8 +1603,8 @@ static int init_types(void) state->expr_type = make_type("expr", state->AST_type, NULL, 0, "expr = BoolOp(boolop op, expr* values)\n" " | NamedExpr(expr target, expr value)\n" - " | BinOp(expr left, operator op, expr right)\n" " | UnaryOp(unaryop op, expr operand)\n" + " | BinOp(expr left, operator op, expr right)\n" " | Lambda(arguments args, expr body)\n" " | IfExp(expr test, expr body, expr orelse)\n" " | Dict(expr* keys, expr* values)\n" @@ -1636,13 +1642,13 @@ static int init_types(void) NamedExpr_fields, 2, "NamedExpr(expr target, expr value)"); if (!state->NamedExpr_type) return 0; - state->BinOp_type = make_type("BinOp", state->expr_type, BinOp_fields, 3, - "BinOp(expr left, operator op, expr right)"); - if (!state->BinOp_type) return 0; state->UnaryOp_type = make_type("UnaryOp", state->expr_type, UnaryOp_fields, 2, "UnaryOp(unaryop op, expr operand)"); if (!state->UnaryOp_type) return 0; + state->BinOp_type = make_type("BinOp", state->expr_type, BinOp_fields, 3, + "BinOp(expr left, operator op, expr right)"); + if (!state->BinOp_type) return 0; state->Lambda_type = make_type("Lambda", state->expr_type, Lambda_fields, 2, "Lambda(arguments args, expr body)"); if (!state->Lambda_type) return 0; @@ -1868,7 +1874,7 @@ static int init_types(void) NULL); if (!state->FloorDiv_singleton) return 0; state->unaryop_type = make_type("unaryop", state->AST_type, NULL, 0, - "unaryop = Invert | Not | UAdd | USub"); + "unaryop = Invert | Not | UAdd | USub | Question"); if (!state->unaryop_type) return 0; if (!add_attributes(state->unaryop_type, NULL, 0)) return 0; state->Invert_type = make_type("Invert", state->unaryop_type, NULL, 0, @@ -1896,6 +1902,13 @@ static int init_types(void) state->USub_singleton = PyType_GenericNew((PyTypeObject *)state->USub_type, NULL, NULL); if (!state->USub_singleton) return 0; + state->Question_type = make_type("Question", state->unaryop_type, NULL, 0, + "Question"); + if (!state->Question_type) return 0; + state->Question_singleton = PyType_GenericNew((PyTypeObject + *)state->Question_type, NULL, + NULL); + if (!state->Question_singleton) return 0; state->cmpop_type = make_type("cmpop", state->AST_type, NULL, 0, "cmpop = Eq | NotEq | Lt | LtE | Gt | GtE | Is | IsNot | In | NotIn"); if (!state->cmpop_type) return 0; @@ -2747,32 +2760,26 @@ NamedExpr(expr_ty target, expr_ty value, int lineno, int col_offset, int } expr_ty -BinOp(expr_ty left, operator_ty op, expr_ty right, int lineno, int col_offset, - int end_lineno, int end_col_offset, PyArena *arena) +UnaryOp(unaryop_ty op, expr_ty operand, int lineno, int col_offset, int + end_lineno, int end_col_offset, PyArena *arena) { expr_ty p; - if (!left) { - PyErr_SetString(PyExc_ValueError, - "field 'left' is required for BinOp"); - return NULL; - } if (!op) { PyErr_SetString(PyExc_ValueError, - "field 'op' is required for BinOp"); + "field 'op' is required for UnaryOp"); return NULL; } - if (!right) { + if (!operand) { PyErr_SetString(PyExc_ValueError, - "field 'right' is required for BinOp"); + "field 'operand' is required for UnaryOp"); return NULL; } p = (expr_ty)PyArena_Malloc(arena, sizeof(*p)); if (!p) return NULL; - p->kind = BinOp_kind; - p->v.BinOp.left = left; - p->v.BinOp.op = op; - p->v.BinOp.right = right; + p->kind = UnaryOp_kind; + p->v.UnaryOp.op = op; + p->v.UnaryOp.operand = operand; p->lineno = lineno; p->col_offset = col_offset; p->end_lineno = end_lineno; @@ -2781,26 +2788,32 @@ BinOp(expr_ty left, operator_ty op, expr_ty right, int lineno, int col_offset, } expr_ty -UnaryOp(unaryop_ty op, expr_ty operand, int lineno, int col_offset, int - end_lineno, int end_col_offset, PyArena *arena) +BinOp(expr_ty left, operator_ty op, expr_ty right, int lineno, int col_offset, + int end_lineno, int end_col_offset, PyArena *arena) { expr_ty p; + if (!left) { + PyErr_SetString(PyExc_ValueError, + "field 'left' is required for BinOp"); + return NULL; + } if (!op) { PyErr_SetString(PyExc_ValueError, - "field 'op' is required for UnaryOp"); + "field 'op' is required for BinOp"); return NULL; } - if (!operand) { + if (!right) { PyErr_SetString(PyExc_ValueError, - "field 'operand' is required for UnaryOp"); + "field 'right' is required for BinOp"); return NULL; } p = (expr_ty)PyArena_Malloc(arena, sizeof(*p)); if (!p) return NULL; - p->kind = UnaryOp_kind; - p->v.UnaryOp.op = op; - p->v.UnaryOp.operand = operand; + p->kind = BinOp_kind; + p->v.BinOp.left = left; + p->v.BinOp.op = op; + p->v.BinOp.right = right; p->lineno = lineno; p->col_offset = col_offset; p->end_lineno = end_lineno; @@ -4167,39 +4180,39 @@ ast2obj_expr(void* _o) goto failed; Py_DECREF(value); break; - case BinOp_kind: - tp = (PyTypeObject *)astmodulestate_global->BinOp_type; + case UnaryOp_kind: + tp = (PyTypeObject *)astmodulestate_global->UnaryOp_type; result = PyType_GenericNew(tp, NULL, NULL); if (!result) goto failed; - value = ast2obj_expr(o->v.BinOp.left); - if (!value) goto failed; - if (PyObject_SetAttr(result, astmodulestate_global->left, value) == -1) - goto failed; - Py_DECREF(value); - value = ast2obj_operator(o->v.BinOp.op); + value = ast2obj_unaryop(o->v.UnaryOp.op); if (!value) goto failed; if (PyObject_SetAttr(result, astmodulestate_global->op, value) == -1) goto failed; Py_DECREF(value); - value = ast2obj_expr(o->v.BinOp.right); + value = ast2obj_expr(o->v.UnaryOp.operand); if (!value) goto failed; - if (PyObject_SetAttr(result, astmodulestate_global->right, value) == -1) + if (PyObject_SetAttr(result, astmodulestate_global->operand, value) == + -1) goto failed; Py_DECREF(value); break; - case UnaryOp_kind: - tp = (PyTypeObject *)astmodulestate_global->UnaryOp_type; + case BinOp_kind: + tp = (PyTypeObject *)astmodulestate_global->BinOp_type; result = PyType_GenericNew(tp, NULL, NULL); if (!result) goto failed; - value = ast2obj_unaryop(o->v.UnaryOp.op); + value = ast2obj_expr(o->v.BinOp.left); + if (!value) goto failed; + if (PyObject_SetAttr(result, astmodulestate_global->left, value) == -1) + goto failed; + Py_DECREF(value); + value = ast2obj_operator(o->v.BinOp.op); if (!value) goto failed; if (PyObject_SetAttr(result, astmodulestate_global->op, value) == -1) goto failed; Py_DECREF(value); - value = ast2obj_expr(o->v.UnaryOp.operand); + value = ast2obj_expr(o->v.BinOp.right); if (!value) goto failed; - if (PyObject_SetAttr(result, astmodulestate_global->operand, value) == - -1) + if (PyObject_SetAttr(result, astmodulestate_global->right, value) == -1) goto failed; Py_DECREF(value); break; @@ -4697,6 +4710,9 @@ PyObject* ast2obj_unaryop(unaryop_ty o) case USub: Py_INCREF(astmodulestate_global->USub_singleton); return astmodulestate_global->USub_singleton; + case Question: + Py_INCREF(astmodulestate_global->Question_singleton); + return astmodulestate_global->Question_singleton; } Py_UNREACHABLE(); } @@ -7434,98 +7450,98 @@ obj2ast_expr(PyObject* obj, expr_ty* out, PyArena* arena) if (*out == NULL) goto failed; return 0; } - tp = astmodulestate_global->BinOp_type; + tp = astmodulestate_global->UnaryOp_type; isinstance = PyObject_IsInstance(obj, tp); if (isinstance == -1) { return 1; } if (isinstance) { - expr_ty left; - operator_ty op; - expr_ty right; + unaryop_ty op; + expr_ty operand; - if (_PyObject_LookupAttr(obj, astmodulestate_global->left, &tmp) < 0) { - return 1; - } - if (tmp == NULL) { - PyErr_SetString(PyExc_TypeError, "required field \"left\" missing from BinOp"); - return 1; - } - else { - int res; - res = obj2ast_expr(tmp, &left, arena); - if (res != 0) goto failed; - Py_CLEAR(tmp); - } if (_PyObject_LookupAttr(obj, astmodulestate_global->op, &tmp) < 0) { return 1; } if (tmp == NULL) { - PyErr_SetString(PyExc_TypeError, "required field \"op\" missing from BinOp"); + PyErr_SetString(PyExc_TypeError, "required field \"op\" missing from UnaryOp"); return 1; } else { int res; - res = obj2ast_operator(tmp, &op, arena); + res = obj2ast_unaryop(tmp, &op, arena); if (res != 0) goto failed; Py_CLEAR(tmp); } - if (_PyObject_LookupAttr(obj, astmodulestate_global->right, &tmp) < 0) { + if (_PyObject_LookupAttr(obj, astmodulestate_global->operand, &tmp) < + 0) { return 1; } if (tmp == NULL) { - PyErr_SetString(PyExc_TypeError, "required field \"right\" missing from BinOp"); + PyErr_SetString(PyExc_TypeError, "required field \"operand\" missing from UnaryOp"); return 1; } else { int res; - res = obj2ast_expr(tmp, &right, arena); + res = obj2ast_expr(tmp, &operand, arena); if (res != 0) goto failed; Py_CLEAR(tmp); } - *out = BinOp(left, op, right, lineno, col_offset, end_lineno, - end_col_offset, arena); + *out = UnaryOp(op, operand, lineno, col_offset, end_lineno, + end_col_offset, arena); if (*out == NULL) goto failed; return 0; } - tp = astmodulestate_global->UnaryOp_type; + tp = astmodulestate_global->BinOp_type; isinstance = PyObject_IsInstance(obj, tp); if (isinstance == -1) { return 1; } if (isinstance) { - unaryop_ty op; - expr_ty operand; + expr_ty left; + operator_ty op; + expr_ty right; + if (_PyObject_LookupAttr(obj, astmodulestate_global->left, &tmp) < 0) { + return 1; + } + if (tmp == NULL) { + PyErr_SetString(PyExc_TypeError, "required field \"left\" missing from BinOp"); + return 1; + } + else { + int res; + res = obj2ast_expr(tmp, &left, arena); + if (res != 0) goto failed; + Py_CLEAR(tmp); + } if (_PyObject_LookupAttr(obj, astmodulestate_global->op, &tmp) < 0) { return 1; } if (tmp == NULL) { - PyErr_SetString(PyExc_TypeError, "required field \"op\" missing from UnaryOp"); + PyErr_SetString(PyExc_TypeError, "required field \"op\" missing from BinOp"); return 1; } else { int res; - res = obj2ast_unaryop(tmp, &op, arena); + res = obj2ast_operator(tmp, &op, arena); if (res != 0) goto failed; Py_CLEAR(tmp); } - if (_PyObject_LookupAttr(obj, astmodulestate_global->operand, &tmp) < - 0) { + if (_PyObject_LookupAttr(obj, astmodulestate_global->right, &tmp) < 0) { return 1; } if (tmp == NULL) { - PyErr_SetString(PyExc_TypeError, "required field \"operand\" missing from UnaryOp"); + PyErr_SetString(PyExc_TypeError, "required field \"right\" missing from BinOp"); return 1; } else { int res; - res = obj2ast_expr(tmp, &operand, arena); + res = obj2ast_expr(tmp, &right, arena); if (res != 0) goto failed; Py_CLEAR(tmp); } - *out = UnaryOp(op, operand, lineno, col_offset, end_lineno, - end_col_offset, arena); + *out = BinOp(left, op, right, lineno, col_offset, end_lineno, + end_col_offset, arena); if (*out == NULL) goto failed; return 0; } @@ -8999,6 +9015,14 @@ obj2ast_unaryop(PyObject* obj, unaryop_ty* out, PyArena* arena) *out = USub; return 0; } + isinstance = PyObject_IsInstance(obj, astmodulestate_global->Question_type); + if (isinstance == -1) { + return 1; + } + if (isinstance) { + *out = Question; + return 0; + } PyErr_Format(PyExc_TypeError, "expected some sort of unaryop, but got %R", obj); return 1; @@ -10084,15 +10108,15 @@ PyInit__ast(void) goto error; } Py_INCREF(astmodulestate(m)->NamedExpr_type); - if (PyModule_AddObject(m, "BinOp", astmodulestate_global->BinOp_type) < 0) { - goto error; - } - Py_INCREF(astmodulestate(m)->BinOp_type); if (PyModule_AddObject(m, "UnaryOp", astmodulestate_global->UnaryOp_type) < 0) { goto error; } Py_INCREF(astmodulestate(m)->UnaryOp_type); + if (PyModule_AddObject(m, "BinOp", astmodulestate_global->BinOp_type) < 0) { + goto error; + } + Py_INCREF(astmodulestate(m)->BinOp_type); if (PyModule_AddObject(m, "Lambda", astmodulestate_global->Lambda_type) < 0) { goto error; @@ -10313,6 +10337,11 @@ PyInit__ast(void) goto error; } Py_INCREF(astmodulestate(m)->USub_type); + if (PyModule_AddObject(m, "Question", astmodulestate_global->Question_type) + < 0) { + goto error; + } + Py_INCREF(astmodulestate(m)->Question_type); if (PyModule_AddObject(m, "cmpop", astmodulestate_global->cmpop_type) < 0) { goto error; } diff --git a/Python/ceval.c b/Python/ceval.c index 0386929a5b2b37..1ebbad4d73f010 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -1624,6 +1624,16 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag) DISPATCH(); } + case TARGET(UNARY_QUESTION): { + PyObject *value = TOP(); + PyObject *res = Py_Optional(value); + Py_DECREF(value); + SET_TOP(res); + if (res == NULL) + goto error; + DISPATCH(); + } + case TARGET(BINARY_POWER): { PyObject *exp = POP(); PyObject *base = TOP(); diff --git a/Python/compile.c b/Python/compile.c index 8fe82f91559e09..cf03f7a31831c3 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -874,6 +874,7 @@ stack_effect(int opcode, int oparg, int jump) return 2; /* Unary operators */ + case UNARY_QUESTION: case UNARY_POSITIVE: case UNARY_NEGATIVE: case UNARY_NOT: @@ -3450,6 +3451,8 @@ unaryop(unaryop_ty op) return UNARY_POSITIVE; case USub: return UNARY_NEGATIVE; + case Question: + return UNARY_QUESTION; default: PyErr_Format(PyExc_SystemError, "unary op %d should not be possible", op); diff --git a/Python/opcode_targets.h b/Python/opcode_targets.h index 538fdbe3e0b5ae..9b8456809967ab 100644 --- a/Python/opcode_targets.h +++ b/Python/opcode_targets.h @@ -13,7 +13,7 @@ static void *opcode_targets[256] = { &&TARGET_UNARY_NEGATIVE, &&TARGET_UNARY_NOT, &&_unknown_opcode, - &&_unknown_opcode, + &&TARGET_UNARY_QUESTION, &&TARGET_UNARY_INVERT, &&TARGET_BINARY_MATRIX_MULTIPLY, &&TARGET_INPLACE_MATRIX_MULTIPLY,