aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/tools/python3/src/Python/symtable.c
diff options
context:
space:
mode:
authorshadchin <shadchin@yandex-team.com>2024-02-12 07:53:52 +0300
committershadchin <shadchin@yandex-team.com>2024-02-12 08:07:36 +0300
commitce1b7ca3171f9158180640c6a02a74b4afffedea (patch)
treee47c1e8391b1b0128262c1e9b1e6ed4c8fff2348 /contrib/tools/python3/src/Python/symtable.c
parent57350d96f030db90f220ce50ee591d5c5d403df7 (diff)
downloadydb-ce1b7ca3171f9158180640c6a02a74b4afffedea.tar.gz
Update Python from 3.11.8 to 3.12.2
Diffstat (limited to 'contrib/tools/python3/src/Python/symtable.c')
-rw-r--r--contrib/tools/python3/src/Python/symtable.c696
1 files changed, 606 insertions, 90 deletions
diff --git a/contrib/tools/python3/src/Python/symtable.c b/contrib/tools/python3/src/Python/symtable.c
index 3519f62098..a5c6b465b7 100644
--- a/contrib/tools/python3/src/Python/symtable.c
+++ b/contrib/tools/python3/src/Python/symtable.c
@@ -1,6 +1,5 @@
#include "Python.h"
#include "pycore_ast.h" // identifier, stmt_ty
-#include "pycore_compile.h" // _Py_Mangle(), _PyFuture_FromAST()
#include "pycore_parser.h" // _PyParser_ASTFromString()
#include "pycore_pystate.h" // _PyThreadState_GET()
#include "pycore_symtable.h" // PySTEntryObject
@@ -36,6 +35,15 @@
#define NAMED_EXPR_COMP_IN_CLASS \
"assignment expression within a comprehension cannot be used in a class body"
+#define NAMED_EXPR_COMP_IN_TYPEVAR_BOUND \
+"assignment expression within a comprehension cannot be used in a TypeVar bound"
+
+#define NAMED_EXPR_COMP_IN_TYPEALIAS \
+"assignment expression within a comprehension cannot be used in a type alias"
+
+#define NAMED_EXPR_COMP_IN_TYPEPARAM \
+"assignment expression within a comprehension cannot be used within the definition of a generic"
+
#define NAMED_EXPR_COMP_CONFLICT \
"assignment expression cannot rebind comprehension iteration variable '%U'"
@@ -46,7 +54,19 @@
"assignment expression cannot be used in a comprehension iterable expression"
#define ANNOTATION_NOT_ALLOWED \
-"'%s' can not be used within an annotation"
+"%s cannot be used within an annotation"
+
+#define TYPEVAR_BOUND_NOT_ALLOWED \
+"%s cannot be used within a TypeVar bound"
+
+#define TYPEALIAS_NOT_ALLOWED \
+"%s cannot be used within a type alias"
+
+#define TYPEPARAM_NOT_ALLOWED \
+"%s cannot be used within the definition of a generic"
+
+#define DUPLICATE_TYPE_PARAM \
+"duplicate type parameter '%U'"
#define LOCATION(x) \
@@ -74,8 +94,7 @@ ste_new(struct symtable *st, identifier name, _Py_block_ty block,
ste->ste_table = st;
ste->ste_id = k; /* ste owns reference to k */
- Py_INCREF(name);
- ste->ste_name = name;
+ ste->ste_name = Py_NewRef(name);
ste->ste_symbols = NULL;
ste->ste_varnames = NULL;
@@ -97,7 +116,7 @@ ste_new(struct symtable *st, identifier name, _Py_block_ty block,
if (st->st_cur != NULL &&
(st->st_cur->ste_nested ||
- st->st_cur->ste_type == FunctionBlock))
+ _PyST_IsFunctionLike(st->st_cur)))
ste->ste_nested = 1;
ste->ste_child_free = 0;
ste->ste_generator = 0;
@@ -105,8 +124,11 @@ ste_new(struct symtable *st, identifier name, _Py_block_ty block,
ste->ste_comprehension = NoComprehension;
ste->ste_returns_value = 0;
ste->ste_needs_class_closure = 0;
+ ste->ste_comp_inlined = 0;
ste->ste_comp_iter_target = 0;
+ ste->ste_can_see_class_scope = 0;
ste->ste_comp_iter_expr = 0;
+ ste->ste_needs_classdict = 0;
ste->ste_symbols = PyDict_New();
ste->ste_varnames = PyList_New(0);
@@ -208,6 +230,7 @@ static int symtable_enter_block(struct symtable *st, identifier name,
static int symtable_exit_block(struct symtable *st);
static int symtable_visit_stmt(struct symtable *st, stmt_ty s);
static int symtable_visit_expr(struct symtable *st, expr_ty s);
+static int symtable_visit_type_param(struct symtable *st, type_param_ty s);
static int symtable_visit_genexp(struct symtable *st, expr_ty s);
static int symtable_visit_listcomp(struct symtable *st, expr_ty s);
static int symtable_visit_setcomp(struct symtable *st, expr_ty s);
@@ -258,17 +281,10 @@ symtable_new(void)
return NULL;
}
-/* When compiling the use of C stack is probably going to be a lot
- lighter than when executing Python code but still can overflow
- and causing a Python crash if not checked (e.g. eval("()"*300000)).
- Using the current recursion limit for the compiler seems too
- restrictive (it caused at least one test to fail) so a factor is
- used to allow deeper recursion when compiling an expression.
-
- Using a scaling factor means this should automatically adjust when
+/* Using a scaling factor means this should automatically adjust when
the recursion limit is adjusted for small or large C stack allocations.
*/
-#define COMPILER_STACK_FRAME_SCALE 3
+#define COMPILER_STACK_FRAME_SCALE 2
struct symtable *
_PySymtable_Build(mod_ty mod, PyObject *filename, PyFutureFeatures *future)
@@ -277,7 +293,6 @@ _PySymtable_Build(mod_ty mod, PyObject *filename, PyFutureFeatures *future)
asdl_stmt_seq *seq;
int i;
PyThreadState *tstate;
- int recursion_limit = Py_GetRecursionLimit();
int starting_recursion_depth;
if (st == NULL)
@@ -286,8 +301,7 @@ _PySymtable_Build(mod_ty mod, PyObject *filename, PyFutureFeatures *future)
_PySymtable_Free(st);
return NULL;
}
- Py_INCREF(filename);
- st->st_filename = filename;
+ st->st_filename = Py_NewRef(filename);
st->st_future = future;
/* Setup recursion depth check counters */
@@ -297,12 +311,10 @@ _PySymtable_Build(mod_ty mod, PyObject *filename, PyFutureFeatures *future)
return NULL;
}
/* Be careful here to prevent overflow. */
- int recursion_depth = tstate->recursion_limit - tstate->recursion_remaining;
- starting_recursion_depth = (recursion_depth < INT_MAX / COMPILER_STACK_FRAME_SCALE) ?
- recursion_depth * COMPILER_STACK_FRAME_SCALE : recursion_depth;
+ int recursion_depth = C_RECURSION_LIMIT - tstate->c_recursion_remaining;
+ starting_recursion_depth = recursion_depth * COMPILER_STACK_FRAME_SCALE;
st->recursion_depth = starting_recursion_depth;
- st->recursion_limit = (recursion_limit < INT_MAX / COMPILER_STACK_FRAME_SCALE) ?
- recursion_limit * COMPILER_STACK_FRAME_SCALE : recursion_limit;
+ st->recursion_limit = C_RECURSION_LIMIT * COMPILER_STACK_FRAME_SCALE;
/* Make the initial symbol information gathering pass */
if (!symtable_enter_block(st, &_Py_ID(top), ModuleBlock, (void *)mod, 0, 0, 0, 0)) {
@@ -377,17 +389,17 @@ PySymtable_Lookup(struct symtable *st, void *key)
if (k == NULL)
return NULL;
v = PyDict_GetItemWithError(st->st_blocks, k);
+ Py_DECREF(k);
+
if (v) {
assert(PySTEntry_Check(v));
- Py_INCREF(v);
}
else if (!PyErr_Occurred()) {
PyErr_SetString(PyExc_KeyError,
"unknown symbol table entry");
}
- Py_DECREF(k);
- return (PySTEntryObject *)v;
+ return (PySTEntryObject *)Py_XNewRef(v);
}
long
@@ -407,6 +419,15 @@ _PyST_GetScope(PySTEntryObject *ste, PyObject *name)
return (symbol >> SCOPE_OFFSET) & SCOPE_MASK;
}
+int
+_PyST_IsFunctionLike(PySTEntryObject *ste)
+{
+ return ste->ste_type == FunctionBlock
+ || ste->ste_type == TypeVarBoundBlock
+ || ste->ste_type == TypeAliasBlock
+ || ste->ste_type == TypeParamBlock;
+}
+
static int
error_at_directive(PySTEntryObject *ste, PyObject *name)
{
@@ -499,7 +520,7 @@ error_at_directive(PySTEntryObject *ste, PyObject *name)
static int
analyze_name(PySTEntryObject *ste, PyObject *scopes, PyObject *name, long flags,
PyObject *bound, PyObject *local, PyObject *free,
- PyObject *global)
+ PyObject *global, PyObject *type_params, PySTEntryObject *class_entry)
{
int contains;
if (flags & DEF_GLOBAL) {
@@ -533,6 +554,16 @@ analyze_name(PySTEntryObject *ste, PyObject *scopes, PyObject *name, long flags,
return error_at_directive(ste, name);
}
+ contains = PySet_Contains(type_params, name);
+ if (contains < 0) {
+ return 0;
+ }
+ if (contains) {
+ PyErr_Format(PyExc_SyntaxError,
+ "nonlocal binding not allowed for type parameter '%U'",
+ name);
+ return error_at_directive(ste, name);
+ }
SET_SCOPE(scopes, name, FREE);
ste->ste_free = 1;
return PySet_Add(free, name) >= 0;
@@ -543,8 +574,34 @@ analyze_name(PySTEntryObject *ste, PyObject *scopes, PyObject *name, long flags,
return 0;
if (PySet_Discard(global, name) < 0)
return 0;
+ if (flags & DEF_TYPE_PARAM) {
+ if (PySet_Add(type_params, name) < 0)
+ return 0;
+ }
+ else {
+ if (PySet_Discard(type_params, name) < 0)
+ return 0;
+ }
return 1;
}
+ // If we were passed class_entry (i.e., we're in an ste_can_see_class_scope scope)
+ // and the bound name is in that set, then the name is potentially bound both by
+ // the immediately enclosing class namespace, and also by an outer function namespace.
+ // In that case, we want the runtime name resolution to look at only the class
+ // namespace and the globals (not the namespace providing the bound).
+ // Similarly, if the name is explicitly global in the class namespace (through the
+ // global statement), we want to also treat it as a global in this scope.
+ if (class_entry != NULL) {
+ long class_flags = _PyST_GetSymbol(class_entry, name);
+ if (class_flags & DEF_GLOBAL) {
+ SET_SCOPE(scopes, name, GLOBAL_EXPLICIT);
+ return 1;
+ }
+ else if (class_flags & DEF_BOUND && !(class_flags & DEF_NONLOCAL)) {
+ SET_SCOPE(scopes, name, GLOBAL_IMPLICIT);
+ return 1;
+ }
+ }
/* If an enclosing block has a binding for this name, it
is a free variable rather than a global variable.
Note that having a non-NULL bound implies that the block
@@ -580,6 +637,75 @@ analyze_name(PySTEntryObject *ste, PyObject *scopes, PyObject *name, long flags,
return 1;
}
+static int
+is_free_in_any_child(PySTEntryObject *entry, PyObject *key)
+{
+ for (Py_ssize_t i = 0; i < PyList_GET_SIZE(entry->ste_children); i++) {
+ PySTEntryObject *child_ste = (PySTEntryObject *)PyList_GET_ITEM(
+ entry->ste_children, i);
+ long scope = _PyST_GetScope(child_ste, key);
+ if (scope == FREE) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int
+inline_comprehension(PySTEntryObject *ste, PySTEntryObject *comp,
+ PyObject *scopes, PyObject *comp_free,
+ PyObject *inlined_cells)
+{
+ PyObject *k, *v;
+ Py_ssize_t pos = 0;
+ while (PyDict_Next(comp->ste_symbols, &pos, &k, &v)) {
+ // skip comprehension parameter
+ long comp_flags = PyLong_AS_LONG(v);
+ if (comp_flags & DEF_PARAM) {
+ assert(_PyUnicode_EqualToASCIIString(k, ".0"));
+ continue;
+ }
+ int scope = (comp_flags >> SCOPE_OFFSET) & SCOPE_MASK;
+ int only_flags = comp_flags & ((1 << SCOPE_OFFSET) - 1);
+ if (scope == CELL || only_flags & DEF_COMP_CELL) {
+ if (PySet_Add(inlined_cells, k) < 0) {
+ return 0;
+ }
+ }
+ PyObject *existing = PyDict_GetItemWithError(ste->ste_symbols, k);
+ if (existing == NULL && PyErr_Occurred()) {
+ return 0;
+ }
+ if (!existing) {
+ // name does not exist in scope, copy from comprehension
+ assert(scope != FREE || PySet_Contains(comp_free, k) == 1);
+ PyObject *v_flags = PyLong_FromLong(only_flags);
+ if (v_flags == NULL) {
+ return 0;
+ }
+ int ok = PyDict_SetItem(ste->ste_symbols, k, v_flags);
+ Py_DECREF(v_flags);
+ if (ok < 0) {
+ return 0;
+ }
+ SET_SCOPE(scopes, k, scope);
+ }
+ else {
+ if (PyLong_AsLong(existing) & DEF_BOUND) {
+ // free vars in comprehension that are locals in outer scope can
+ // now simply be locals, unless they are free in comp children,
+ // or if the outer scope is a class block
+ if (!is_free_in_any_child(comp, k) && ste->ste_type != ClassBlock) {
+ if (PySet_Discard(comp_free, k) < 0) {
+ return 0;
+ }
+ }
+ }
+ }
+ }
+ return 1;
+}
+
#undef SET_SCOPE
/* If a name is defined in free and also in locals, then this block
@@ -591,7 +717,7 @@ analyze_name(PySTEntryObject *ste, PyObject *scopes, PyObject *name, long flags,
*/
static int
-analyze_cells(PyObject *scopes, PyObject *free)
+analyze_cells(PyObject *scopes, PyObject *free, PyObject *inlined_cells)
{
PyObject *name, *v, *v_cell;
int success = 0;
@@ -611,7 +737,13 @@ analyze_cells(PyObject *scopes, PyObject *free)
goto error;
}
if (!contains) {
- continue;
+ contains = PySet_Contains(inlined_cells, name);
+ if (contains < 0) {
+ goto error;
+ }
+ if (!contains) {
+ continue;
+ }
}
/* Replace LOCAL with CELL for this name, and remove
from free. It is safe to replace the value of name
@@ -637,6 +769,11 @@ drop_class_free(PySTEntryObject *ste, PyObject *free)
return 0;
if (res)
ste->ste_needs_class_closure = 1;
+ res = PySet_Discard(free, &_Py_ID(__classdict__));
+ if (res < 0)
+ return 0;
+ if (res)
+ ste->ste_needs_classdict = 1;
return 1;
}
@@ -646,7 +783,8 @@ drop_class_free(PySTEntryObject *ste, PyObject *free)
*/
static int
update_symbols(PyObject *symbols, PyObject *scopes,
- PyObject *bound, PyObject *free, int classflag)
+ PyObject *bound, PyObject *free,
+ PyObject *inlined_cells, int classflag)
{
PyObject *name = NULL, *itr = NULL;
PyObject *v = NULL, *v_scope = NULL, *v_new = NULL, *v_free = NULL;
@@ -657,6 +795,13 @@ update_symbols(PyObject *symbols, PyObject *scopes,
long scope, flags;
assert(PyLong_Check(v));
flags = PyLong_AS_LONG(v);
+ int contains = PySet_Contains(inlined_cells, name);
+ if (contains < 0) {
+ return 0;
+ }
+ if (contains) {
+ flags |= DEF_COMP_CELL;
+ }
v_scope = PyDict_GetItemWithError(scopes, name);
assert(v_scope && PyLong_Check(v_scope));
scope = PyLong_AS_LONG(v_scope);
@@ -691,8 +836,7 @@ update_symbols(PyObject *symbols, PyObject *scopes,
the class that has the same name as a local
or global in the class scope.
*/
- if (classflag &&
- PyLong_AS_LONG(v) & (DEF_BOUND | DEF_GLOBAL)) {
+ if (classflag) {
long flags = PyLong_AS_LONG(v) | DEF_FREE_CLASS;
v_new = PyLong_FromLong(flags);
if (!v_new) {
@@ -766,17 +910,19 @@ error:
static int
analyze_child_block(PySTEntryObject *entry, PyObject *bound, PyObject *free,
- PyObject *global, PyObject* child_free);
+ PyObject *global, PyObject *type_params,
+ PySTEntryObject *class_entry, PyObject **child_free);
static int
analyze_block(PySTEntryObject *ste, PyObject *bound, PyObject *free,
- PyObject *global)
+ PyObject *global, PyObject *type_params,
+ PySTEntryObject *class_entry)
{
PyObject *name, *v, *local = NULL, *scopes = NULL, *newbound = NULL;
- PyObject *newglobal = NULL, *newfree = NULL, *allfree = NULL;
+ PyObject *newglobal = NULL, *newfree = NULL, *inlined_cells = NULL;
PyObject *temp;
- int i, success = 0;
- Py_ssize_t pos = 0;
+ int success = 0;
+ Py_ssize_t i, pos = 0;
local = PySet_New(NULL); /* collect new names bound in block */
if (!local)
@@ -785,8 +931,8 @@ analyze_block(PySTEntryObject *ste, PyObject *bound, PyObject *free,
if (!scopes)
goto error;
- /* Allocate new global and bound variable dictionaries. These
- dictionaries hold the names visible in nested blocks. For
+ /* Allocate new global, bound and free variable sets. These
+ sets hold the names visible in nested blocks. For
ClassBlocks, the bound and global names are initialized
before analyzing names, because class bindings aren't
visible in methods. For other blocks, they are initialized
@@ -805,6 +951,9 @@ analyze_block(PySTEntryObject *ste, PyObject *bound, PyObject *free,
newbound = PySet_New(NULL);
if (!newbound)
goto error;
+ inlined_cells = PySet_New(NULL);
+ if (!inlined_cells)
+ goto error;
/* Class namespace has no effect on names visible in
nested functions, so populate the global and bound
@@ -829,14 +978,14 @@ analyze_block(PySTEntryObject *ste, PyObject *bound, PyObject *free,
while (PyDict_Next(ste->ste_symbols, &pos, &name, &v)) {
long flags = PyLong_AS_LONG(v);
if (!analyze_name(ste, scopes, name, flags,
- bound, local, free, global))
+ bound, local, free, global, type_params, class_entry))
goto error;
}
/* Populate global and bound sets to be passed to children. */
if (ste->ste_type != ClassBlock) {
/* Add function locals to bound set */
- if (ste->ste_type == FunctionBlock) {
+ if (_PyST_IsFunctionLike(ste)) {
temp = PyNumber_InPlaceOr(newbound, local);
if (!temp)
goto error;
@@ -856,46 +1005,85 @@ analyze_block(PySTEntryObject *ste, PyObject *bound, PyObject *free,
Py_DECREF(temp);
}
else {
- /* Special-case __class__ */
+ /* Special-case __class__ and __classdict__ */
if (PySet_Add(newbound, &_Py_ID(__class__)) < 0)
goto error;
+ if (PySet_Add(newbound, &_Py_ID(__classdict__)) < 0)
+ goto error;
}
/* Recursively call analyze_child_block() on each child block.
newbound, newglobal now contain the names visible in
nested blocks. The free variables in the children will
- be collected in allfree.
+ be added to newfree.
*/
- allfree = PySet_New(NULL);
- if (!allfree)
- goto error;
for (i = 0; i < PyList_GET_SIZE(ste->ste_children); ++i) {
+ PyObject *child_free = NULL;
PyObject *c = PyList_GET_ITEM(ste->ste_children, i);
PySTEntryObject* entry;
assert(c && PySTEntry_Check(c));
entry = (PySTEntryObject*)c;
+
+ PySTEntryObject *new_class_entry = NULL;
+ if (entry->ste_can_see_class_scope) {
+ if (ste->ste_type == ClassBlock) {
+ new_class_entry = ste;
+ }
+ else if (class_entry) {
+ new_class_entry = class_entry;
+ }
+ }
+
+ // we inline all non-generator-expression comprehensions
+ int inline_comp =
+ entry->ste_comprehension &&
+ !entry->ste_generator;
+
if (!analyze_child_block(entry, newbound, newfree, newglobal,
- allfree))
+ type_params, new_class_entry, &child_free))
+ {
+ goto error;
+ }
+ if (inline_comp) {
+ if (!inline_comprehension(ste, entry, scopes, child_free, inlined_cells)) {
+ Py_DECREF(child_free);
+ goto error;
+ }
+ entry->ste_comp_inlined = 1;
+ }
+ temp = PyNumber_InPlaceOr(newfree, child_free);
+ Py_DECREF(child_free);
+ if (!temp)
goto error;
+ Py_DECREF(temp);
/* Check if any children have free variables */
if (entry->ste_free || entry->ste_child_free)
ste->ste_child_free = 1;
}
- temp = PyNumber_InPlaceOr(newfree, allfree);
- if (!temp)
- goto error;
- Py_DECREF(temp);
+ /* Splice children of inlined comprehensions into our children list */
+ for (i = PyList_GET_SIZE(ste->ste_children) - 1; i >= 0; --i) {
+ PyObject* c = PyList_GET_ITEM(ste->ste_children, i);
+ PySTEntryObject* entry;
+ assert(c && PySTEntry_Check(c));
+ entry = (PySTEntryObject*)c;
+ if (entry->ste_comp_inlined &&
+ PyList_SetSlice(ste->ste_children, i, i + 1,
+ entry->ste_children) < 0)
+ {
+ goto error;
+ }
+ }
/* Check if any local variables must be converted to cell variables */
- if (ste->ste_type == FunctionBlock && !analyze_cells(scopes, newfree))
+ if (_PyST_IsFunctionLike(ste) && !analyze_cells(scopes, newfree, inlined_cells))
goto error;
else if (ste->ste_type == ClassBlock && !drop_class_free(ste, newfree))
goto error;
/* Records the results of the analysis in the symbol table entry */
- if (!update_symbols(ste->ste_symbols, scopes, bound, newfree,
- ste->ste_type == ClassBlock))
+ if (!update_symbols(ste->ste_symbols, scopes, bound, newfree, inlined_cells,
+ (ste->ste_type == ClassBlock) || ste->ste_can_see_class_scope))
goto error;
temp = PyNumber_InPlaceOr(free, newfree);
@@ -909,7 +1097,7 @@ analyze_block(PySTEntryObject *ste, PyObject *bound, PyObject *free,
Py_XDECREF(newbound);
Py_XDECREF(newglobal);
Py_XDECREF(newfree);
- Py_XDECREF(allfree);
+ Py_XDECREF(inlined_cells);
if (!success)
assert(PyErr_Occurred());
return success;
@@ -917,16 +1105,17 @@ analyze_block(PySTEntryObject *ste, PyObject *bound, PyObject *free,
static int
analyze_child_block(PySTEntryObject *entry, PyObject *bound, PyObject *free,
- PyObject *global, PyObject* child_free)
+ PyObject *global, PyObject *type_params,
+ PySTEntryObject *class_entry, PyObject** child_free)
{
PyObject *temp_bound = NULL, *temp_global = NULL, *temp_free = NULL;
- PyObject *temp;
+ PyObject *temp_type_params = NULL;
- /* Copy the bound and global dictionaries.
+ /* Copy the bound/global/free sets.
- These dictionaries are used by all blocks enclosed by the
+ These sets are used by all blocks enclosed by the
current block. The analyze_block() call modifies these
- dictionaries.
+ sets.
*/
temp_bound = PySet_New(bound);
@@ -938,28 +1127,30 @@ analyze_child_block(PySTEntryObject *entry, PyObject *bound, PyObject *free,
temp_global = PySet_New(global);
if (!temp_global)
goto error;
-
- if (!analyze_block(entry, temp_bound, temp_free, temp_global))
+ temp_type_params = PySet_New(type_params);
+ if (!temp_type_params)
goto error;
- temp = PyNumber_InPlaceOr(child_free, temp_free);
- if (!temp)
+
+ if (!analyze_block(entry, temp_bound, temp_free, temp_global,
+ temp_type_params, class_entry))
goto error;
- Py_DECREF(temp);
+ *child_free = temp_free;
Py_DECREF(temp_bound);
- Py_DECREF(temp_free);
Py_DECREF(temp_global);
+ Py_DECREF(temp_type_params);
return 1;
error:
Py_XDECREF(temp_bound);
Py_XDECREF(temp_free);
Py_XDECREF(temp_global);
+ Py_XDECREF(temp_type_params);
return 0;
}
static int
symtable_analyze(struct symtable *st)
{
- PyObject *free, *global;
+ PyObject *free, *global, *type_params;
int r;
free = PySet_New(NULL);
@@ -970,9 +1161,16 @@ symtable_analyze(struct symtable *st)
Py_DECREF(free);
return 0;
}
- r = analyze_block(st->st_top, NULL, free, global);
+ type_params = PySet_New(NULL);
+ if (!type_params) {
+ Py_DECREF(free);
+ Py_DECREF(global);
+ return 0;
+ }
+ r = analyze_block(st->st_top, NULL, free, global, type_params, NULL);
Py_DECREF(free);
Py_DECREF(global);
+ Py_DECREF(type_params);
return r;
}
@@ -1075,6 +1273,13 @@ symtable_add_def_helper(struct symtable *st, PyObject *name, int flag, struct _s
end_lineno, end_col_offset + 1);
goto error;
}
+ if ((flag & DEF_TYPE_PARAM) && (val & DEF_TYPE_PARAM)) {
+ PyErr_Format(PyExc_SyntaxError, DUPLICATE_TYPE_PARAM, name);
+ PyErr_RangedSyntaxLocationObject(st->st_filename,
+ lineno, col_offset + 1,
+ end_lineno, end_col_offset + 1);
+ goto error;
+ }
val |= flag;
}
else if (PyErr_Occurred()) {
@@ -1146,6 +1351,65 @@ symtable_add_def(struct symtable *st, PyObject *name, int flag,
lineno, col_offset, end_lineno, end_col_offset);
}
+static int
+symtable_enter_type_param_block(struct symtable *st, identifier name,
+ void *ast, int has_defaults, int has_kwdefaults,
+ enum _stmt_kind kind,
+ int lineno, int col_offset,
+ int end_lineno, int end_col_offset)
+{
+ _Py_block_ty current_type = st->st_cur->ste_type;
+ if(!symtable_enter_block(st, name, TypeParamBlock, ast, lineno,
+ col_offset, end_lineno, end_col_offset)) {
+ return 0;
+ }
+ if (current_type == ClassBlock) {
+ st->st_cur->ste_can_see_class_scope = 1;
+ if (!symtable_add_def(st, &_Py_ID(__classdict__), USE, lineno, col_offset, end_lineno, end_col_offset)) {
+ return 0;
+ }
+ }
+ if (kind == ClassDef_kind) {
+ _Py_DECLARE_STR(type_params, ".type_params");
+ // It gets "set" when we create the type params tuple and
+ // "used" when we build up the bases.
+ if (!symtable_add_def(st, &_Py_STR(type_params), DEF_LOCAL,
+ lineno, col_offset, end_lineno, end_col_offset)) {
+ return 0;
+ }
+ if (!symtable_add_def(st, &_Py_STR(type_params), USE,
+ lineno, col_offset, end_lineno, end_col_offset)) {
+ return 0;
+ }
+ st->st_private = name;
+ // This is used for setting the generic base
+ _Py_DECLARE_STR(generic_base, ".generic_base");
+ if (!symtable_add_def(st, &_Py_STR(generic_base), DEF_LOCAL,
+ lineno, col_offset, end_lineno, end_col_offset)) {
+ return 0;
+ }
+ if (!symtable_add_def(st, &_Py_STR(generic_base), USE,
+ lineno, col_offset, end_lineno, end_col_offset)) {
+ return 0;
+ }
+ }
+ if (has_defaults) {
+ _Py_DECLARE_STR(defaults, ".defaults");
+ if (!symtable_add_def(st, &_Py_STR(defaults), DEF_PARAM,
+ lineno, col_offset, end_lineno, end_col_offset)) {
+ return 0;
+ }
+ }
+ if (has_kwdefaults) {
+ _Py_DECLARE_STR(kwdefaults, ".kwdefaults");
+ if (!symtable_add_def(st, &_Py_STR(kwdefaults), DEF_PARAM,
+ lineno, col_offset, end_lineno, end_col_offset)) {
+ return 0;
+ }
+ }
+ return 1;
+}
+
/* VISIT, VISIT_SEQ and VIST_SEQ_TAIL take an ASDL type as their second argument.
They use the ASDL name to synthesize the name of the C type and the visit
function.
@@ -1217,6 +1481,17 @@ symtable_record_directive(struct symtable *st, identifier name, int lineno,
return res == 0;
}
+static int
+has_kwonlydefaults(asdl_arg_seq *kwonlyargs, asdl_expr_seq *kw_defaults)
+{
+ for (int i = 0; i < asdl_seq_LEN(kwonlyargs); i++) {
+ expr_ty default_ = asdl_seq_GET(kw_defaults, i);
+ if (default_) {
+ return 1;
+ }
+ }
+ return 0;
+}
static int
symtable_visit_stmt(struct symtable *st, stmt_ty s)
@@ -1234,11 +1509,24 @@ symtable_visit_stmt(struct symtable *st, stmt_ty s)
VISIT_SEQ(st, expr, s->v.FunctionDef.args->defaults);
if (s->v.FunctionDef.args->kw_defaults)
VISIT_SEQ_WITH_NULL(st, expr, s->v.FunctionDef.args->kw_defaults);
+ if (s->v.FunctionDef.decorator_list)
+ VISIT_SEQ(st, expr, s->v.FunctionDef.decorator_list);
+ if (asdl_seq_LEN(s->v.FunctionDef.type_params) > 0) {
+ if (!symtable_enter_type_param_block(
+ st, s->v.FunctionDef.name,
+ (void *)s->v.FunctionDef.type_params,
+ s->v.FunctionDef.args->defaults != NULL,
+ has_kwonlydefaults(s->v.FunctionDef.args->kwonlyargs,
+ s->v.FunctionDef.args->kw_defaults),
+ s->kind,
+ LOCATION(s))) {
+ VISIT_QUIT(st, 0);
+ }
+ VISIT_SEQ(st, type_param, s->v.FunctionDef.type_params);
+ }
if (!symtable_visit_annotations(st, s, s->v.FunctionDef.args,
s->v.FunctionDef.returns))
VISIT_QUIT(st, 0);
- if (s->v.FunctionDef.decorator_list)
- VISIT_SEQ(st, expr, s->v.FunctionDef.decorator_list);
if (!symtable_enter_block(st, s->v.FunctionDef.name,
FunctionBlock, (void *)s,
LOCATION(s)))
@@ -1247,25 +1535,85 @@ symtable_visit_stmt(struct symtable *st, stmt_ty s)
VISIT_SEQ(st, stmt, s->v.FunctionDef.body);
if (!symtable_exit_block(st))
VISIT_QUIT(st, 0);
+ if (asdl_seq_LEN(s->v.FunctionDef.type_params) > 0) {
+ if (!symtable_exit_block(st))
+ VISIT_QUIT(st, 0);
+ }
break;
case ClassDef_kind: {
PyObject *tmp;
if (!symtable_add_def(st, s->v.ClassDef.name, DEF_LOCAL, LOCATION(s)))
VISIT_QUIT(st, 0);
- VISIT_SEQ(st, expr, s->v.ClassDef.bases);
- VISIT_SEQ(st, keyword, s->v.ClassDef.keywords);
if (s->v.ClassDef.decorator_list)
VISIT_SEQ(st, expr, s->v.ClassDef.decorator_list);
+ if (asdl_seq_LEN(s->v.ClassDef.type_params) > 0) {
+ if (!symtable_enter_type_param_block(st, s->v.ClassDef.name,
+ (void *)s->v.ClassDef.type_params,
+ false, false, s->kind,
+ LOCATION(s))) {
+ VISIT_QUIT(st, 0);
+ }
+ VISIT_SEQ(st, type_param, s->v.ClassDef.type_params);
+ }
+ VISIT_SEQ(st, expr, s->v.ClassDef.bases);
+ VISIT_SEQ(st, keyword, s->v.ClassDef.keywords);
if (!symtable_enter_block(st, s->v.ClassDef.name, ClassBlock,
(void *)s, s->lineno, s->col_offset,
s->end_lineno, s->end_col_offset))
VISIT_QUIT(st, 0);
tmp = st->st_private;
st->st_private = s->v.ClassDef.name;
+ if (asdl_seq_LEN(s->v.ClassDef.type_params) > 0) {
+ if (!symtable_add_def(st, &_Py_ID(__type_params__),
+ DEF_LOCAL, LOCATION(s))) {
+ VISIT_QUIT(st, 0);
+ }
+ _Py_DECLARE_STR(type_params, ".type_params");
+ if (!symtable_add_def(st, &_Py_STR(type_params),
+ USE, LOCATION(s))) {
+ VISIT_QUIT(st, 0);
+ }
+ }
VISIT_SEQ(st, stmt, s->v.ClassDef.body);
st->st_private = tmp;
if (!symtable_exit_block(st))
VISIT_QUIT(st, 0);
+ if (asdl_seq_LEN(s->v.ClassDef.type_params) > 0) {
+ if (!symtable_exit_block(st))
+ VISIT_QUIT(st, 0);
+ }
+ break;
+ }
+ case TypeAlias_kind: {
+ VISIT(st, expr, s->v.TypeAlias.name);
+ assert(s->v.TypeAlias.name->kind == Name_kind);
+ PyObject *name = s->v.TypeAlias.name->v.Name.id;
+ int is_in_class = st->st_cur->ste_type == ClassBlock;
+ int is_generic = asdl_seq_LEN(s->v.TypeAlias.type_params) > 0;
+ if (is_generic) {
+ if (!symtable_enter_type_param_block(
+ st, name,
+ (void *)s->v.TypeAlias.type_params,
+ false, false, s->kind,
+ LOCATION(s))) {
+ VISIT_QUIT(st, 0);
+ }
+ VISIT_SEQ(st, type_param, s->v.TypeAlias.type_params);
+ }
+ if (!symtable_enter_block(st, name, TypeAliasBlock,
+ (void *)s, LOCATION(s)))
+ VISIT_QUIT(st, 0);
+ st->st_cur->ste_can_see_class_scope = is_in_class;
+ if (is_in_class && !symtable_add_def(st, &_Py_ID(__classdict__), USE, LOCATION(s->v.TypeAlias.value))) {
+ VISIT_QUIT(st, 0);
+ }
+ VISIT(st, expr, s->v.TypeAlias.value);
+ if (!symtable_exit_block(st))
+ VISIT_QUIT(st, 0);
+ if (is_generic) {
+ if (!symtable_exit_block(st))
+ VISIT_QUIT(st, 0);
+ }
break;
}
case Return_kind:
@@ -1474,11 +1822,24 @@ symtable_visit_stmt(struct symtable *st, stmt_ty s)
if (s->v.AsyncFunctionDef.args->kw_defaults)
VISIT_SEQ_WITH_NULL(st, expr,
s->v.AsyncFunctionDef.args->kw_defaults);
+ if (s->v.AsyncFunctionDef.decorator_list)
+ VISIT_SEQ(st, expr, s->v.AsyncFunctionDef.decorator_list);
+ if (asdl_seq_LEN(s->v.AsyncFunctionDef.type_params) > 0) {
+ if (!symtable_enter_type_param_block(
+ st, s->v.AsyncFunctionDef.name,
+ (void *)s->v.AsyncFunctionDef.type_params,
+ s->v.AsyncFunctionDef.args->defaults != NULL,
+ has_kwonlydefaults(s->v.AsyncFunctionDef.args->kwonlyargs,
+ s->v.AsyncFunctionDef.args->kw_defaults),
+ s->kind,
+ LOCATION(s))) {
+ VISIT_QUIT(st, 0);
+ }
+ VISIT_SEQ(st, type_param, s->v.AsyncFunctionDef.type_params);
+ }
if (!symtable_visit_annotations(st, s, s->v.AsyncFunctionDef.args,
s->v.AsyncFunctionDef.returns))
VISIT_QUIT(st, 0);
- if (s->v.AsyncFunctionDef.decorator_list)
- VISIT_SEQ(st, expr, s->v.AsyncFunctionDef.decorator_list);
if (!symtable_enter_block(st, s->v.AsyncFunctionDef.name,
FunctionBlock, (void *)s,
s->lineno, s->col_offset,
@@ -1489,6 +1850,10 @@ symtable_visit_stmt(struct symtable *st, stmt_ty s)
VISIT_SEQ(st, stmt, s->v.AsyncFunctionDef.body);
if (!symtable_exit_block(st))
VISIT_QUIT(st, 0);
+ if (asdl_seq_LEN(s->v.AsyncFunctionDef.type_params) > 0) {
+ if (!symtable_exit_block(st))
+ VISIT_QUIT(st, 0);
+ }
break;
case AsyncWith_kind:
VISIT_SEQ(st, withitem, s->v.AsyncWith.items);
@@ -1526,7 +1891,8 @@ symtable_extend_namedexpr_scope(struct symtable *st, expr_ty e)
*/
if (ste->ste_comprehension) {
long target_in_scope = _PyST_GetSymbol(ste, target_name);
- if (target_in_scope & DEF_COMP_ITER) {
+ if ((target_in_scope & DEF_COMP_ITER) &&
+ (target_in_scope & DEF_LOCAL)) {
PyErr_Format(PyExc_SyntaxError, NAMED_EXPR_COMP_CONFLICT, target_name);
PyErr_RangedSyntaxLocationObject(st->st_filename,
e->lineno,
@@ -1562,9 +1928,27 @@ symtable_extend_namedexpr_scope(struct symtable *st, expr_ty e)
return symtable_add_def_helper(st, target_name, DEF_GLOBAL, ste, LOCATION(e));
}
- /* Disallow usage in ClassBlock */
- if (ste->ste_type == ClassBlock) {
- PyErr_Format(PyExc_SyntaxError, NAMED_EXPR_COMP_IN_CLASS);
+ /* Disallow usage in ClassBlock and type scopes */
+ if (ste->ste_type == ClassBlock ||
+ ste->ste_type == TypeParamBlock ||
+ ste->ste_type == TypeAliasBlock ||
+ ste->ste_type == TypeVarBoundBlock) {
+ switch (ste->ste_type) {
+ case ClassBlock:
+ PyErr_Format(PyExc_SyntaxError, NAMED_EXPR_COMP_IN_CLASS);
+ break;
+ case TypeParamBlock:
+ PyErr_Format(PyExc_SyntaxError, NAMED_EXPR_COMP_IN_TYPEPARAM);
+ break;
+ case TypeAliasBlock:
+ PyErr_Format(PyExc_SyntaxError, NAMED_EXPR_COMP_IN_TYPEALIAS);
+ break;
+ case TypeVarBoundBlock:
+ PyErr_Format(PyExc_SyntaxError, NAMED_EXPR_COMP_IN_TYPEVAR_BOUND);
+ break;
+ default:
+ Py_UNREACHABLE();
+ }
PyErr_RangedSyntaxLocationObject(st->st_filename,
e->lineno,
e->col_offset + 1,
@@ -1574,10 +1958,10 @@ symtable_extend_namedexpr_scope(struct symtable *st, expr_ty e)
}
}
- /* We should always find either a FunctionBlock, ModuleBlock or ClassBlock
+ /* We should always find either a function-like block, ModuleBlock or ClassBlock
and should never fall to this case
*/
- assert(0);
+ Py_UNREACHABLE();
return 0;
}
@@ -1631,6 +2015,17 @@ symtable_visit_expr(struct symtable *st, expr_ty e)
VISIT(st, expr, e->v.UnaryOp.operand);
break;
case Lambda_kind: {
+ if (st->st_cur->ste_can_see_class_scope) {
+ // gh-109118
+ PyErr_Format(PyExc_SyntaxError,
+ "Cannot use lambda in annotation scope within class scope");
+ PyErr_RangedSyntaxLocationObject(st->st_filename,
+ e->lineno,
+ e->col_offset + 1,
+ e->end_lineno,
+ e->end_col_offset + 1);
+ VISIT_QUIT(st, 0);
+ }
if (e->v.Lambda.args->defaults)
VISIT_SEQ(st, expr, e->v.Lambda.args->defaults);
if (e->v.Lambda.args->kw_defaults)
@@ -1747,7 +2142,7 @@ symtable_visit_expr(struct symtable *st, expr_ty e)
VISIT_QUIT(st, 0);
/* Special-case super: it counts as a use of __class__ */
if (e->v.Name.ctx == Load &&
- st->st_cur->ste_type == FunctionBlock &&
+ _PyST_IsFunctionLike(st->st_cur) &&
_PyUnicode_EqualToASCIIString(e->v.Name.id, "super")) {
if (!symtable_add_def(st, &_Py_ID(__class__), USE, LOCATION(e)))
VISIT_QUIT(st, 0);
@@ -1765,6 +2160,45 @@ symtable_visit_expr(struct symtable *st, expr_ty e)
}
static int
+symtable_visit_type_param(struct symtable *st, type_param_ty tp)
+{
+ if (++st->recursion_depth > st->recursion_limit) {
+ PyErr_SetString(PyExc_RecursionError,
+ "maximum recursion depth exceeded during compilation");
+ VISIT_QUIT(st, 0);
+ }
+ switch(tp->kind) {
+ case TypeVar_kind:
+ if (!symtable_add_def(st, tp->v.TypeVar.name, DEF_TYPE_PARAM | DEF_LOCAL, LOCATION(tp)))
+ VISIT_QUIT(st, 0);
+ if (tp->v.TypeVar.bound) {
+ int is_in_class = st->st_cur->ste_can_see_class_scope;
+ if (!symtable_enter_block(st, tp->v.TypeVar.name,
+ TypeVarBoundBlock, (void *)tp,
+ LOCATION(tp)))
+ VISIT_QUIT(st, 0);
+ st->st_cur->ste_can_see_class_scope = is_in_class;
+ if (is_in_class && !symtable_add_def(st, &_Py_ID(__classdict__), USE, LOCATION(tp->v.TypeVar.bound))) {
+ VISIT_QUIT(st, 0);
+ }
+ VISIT(st, expr, tp->v.TypeVar.bound);
+ if (!symtable_exit_block(st))
+ VISIT_QUIT(st, 0);
+ }
+ break;
+ case TypeVarTuple_kind:
+ if (!symtable_add_def(st, tp->v.TypeVarTuple.name, DEF_TYPE_PARAM | DEF_LOCAL, LOCATION(tp)))
+ VISIT_QUIT(st, 0);
+ break;
+ case ParamSpec_kind:
+ if (!symtable_add_def(st, tp->v.ParamSpec.name, DEF_TYPE_PARAM | DEF_LOCAL, LOCATION(tp)))
+ VISIT_QUIT(st, 0);
+ break;
+ }
+ VISIT_QUIT(st, 1);
+}
+
+static int
symtable_visit_pattern(struct symtable *st, pattern_ty p)
{
if (++st->recursion_depth > st->recursion_limit) {
@@ -1985,8 +2419,7 @@ symtable_visit_alias(struct symtable *st, alias_ty a)
return 0;
}
else {
- store_name = name;
- Py_INCREF(store_name);
+ store_name = Py_NewRef(name);
}
if (!_PyUnicode_EqualToASCIIString(name, "*")) {
int r = symtable_add_def(st, store_name, DEF_IMPORT, LOCATION(a));
@@ -2042,6 +2475,18 @@ symtable_handle_comprehension(struct symtable *st, expr_ty e,
identifier scope_name, asdl_comprehension_seq *generators,
expr_ty elt, expr_ty value)
{
+ if (st->st_cur->ste_can_see_class_scope) {
+ // gh-109118
+ PyErr_Format(PyExc_SyntaxError,
+ "Cannot use comprehension in annotation scope within class scope");
+ PyErr_RangedSyntaxLocationObject(st->st_filename,
+ e->lineno,
+ e->col_offset + 1,
+ e->end_lineno,
+ e->end_col_offset + 1);
+ VISIT_QUIT(st, 0);
+ }
+
int is_generator = (e->kind == GeneratorExp_kind);
comprehension_ty outermost = ((comprehension_ty)
asdl_seq_GET(generators, 0));
@@ -2136,11 +2581,18 @@ symtable_visit_dictcomp(struct symtable *st, expr_ty e)
static int
symtable_raise_if_annotation_block(struct symtable *st, const char *name, expr_ty e)
{
- if (st->st_cur->ste_type != AnnotationBlock) {
+ enum _block_type type = st->st_cur->ste_type;
+ if (type == AnnotationBlock)
+ PyErr_Format(PyExc_SyntaxError, ANNOTATION_NOT_ALLOWED, name);
+ else if (type == TypeVarBoundBlock)
+ PyErr_Format(PyExc_SyntaxError, TYPEVAR_BOUND_NOT_ALLOWED, name);
+ else if (type == TypeAliasBlock)
+ PyErr_Format(PyExc_SyntaxError, TYPEALIAS_NOT_ALLOWED, name);
+ else if (type == TypeParamBlock)
+ PyErr_Format(PyExc_SyntaxError, TYPEPARAM_NOT_ALLOWED, name);
+ else
return 1;
- }
- PyErr_Format(PyExc_SyntaxError, ANNOTATION_NOT_ALLOWED, name);
PyErr_RangedSyntaxLocationObject(st->st_filename,
e->lineno,
e->col_offset + 1,
@@ -2180,14 +2632,78 @@ _Py_SymtableStringObjectFlags(const char *str, PyObject *filename,
_PyArena_Free(arena);
return NULL;
}
- PyFutureFeatures *future = _PyFuture_FromAST(mod, filename);
- if (future == NULL) {
+ PyFutureFeatures future;
+ if (!_PyFuture_FromAST(mod, filename, &future)) {
_PyArena_Free(arena);
return NULL;
}
- future->ff_features |= flags->cf_flags;
- st = _PySymtable_Build(mod, filename, future);
- PyObject_Free((void *)future);
+ future.ff_features |= flags->cf_flags;
+ st = _PySymtable_Build(mod, filename, &future);
_PyArena_Free(arena);
return st;
}
+
+PyObject *
+_Py_Mangle(PyObject *privateobj, PyObject *ident)
+{
+ /* Name mangling: __private becomes _classname__private.
+ This is independent from how the name is used. */
+ if (privateobj == NULL || !PyUnicode_Check(privateobj) ||
+ PyUnicode_READ_CHAR(ident, 0) != '_' ||
+ PyUnicode_READ_CHAR(ident, 1) != '_') {
+ return Py_NewRef(ident);
+ }
+ size_t nlen = PyUnicode_GET_LENGTH(ident);
+ size_t plen = PyUnicode_GET_LENGTH(privateobj);
+ /* Don't mangle __id__ or names with dots.
+
+ The only time a name with a dot can occur is when
+ we are compiling an import statement that has a
+ package name.
+
+ TODO(jhylton): Decide whether we want to support
+ mangling of the module name, e.g. __M.X.
+ */
+ if ((PyUnicode_READ_CHAR(ident, nlen-1) == '_' &&
+ PyUnicode_READ_CHAR(ident, nlen-2) == '_') ||
+ PyUnicode_FindChar(ident, '.', 0, nlen, 1) != -1) {
+ return Py_NewRef(ident); /* Don't mangle __whatever__ */
+ }
+ /* Strip leading underscores from class name */
+ size_t ipriv = 0;
+ while (PyUnicode_READ_CHAR(privateobj, ipriv) == '_') {
+ ipriv++;
+ }
+ if (ipriv == plen) {
+ return Py_NewRef(ident); /* Don't mangle if class is just underscores */
+ }
+ plen -= ipriv;
+
+ if (plen + nlen >= PY_SSIZE_T_MAX - 1) {
+ PyErr_SetString(PyExc_OverflowError,
+ "private identifier too large to be mangled");
+ return NULL;
+ }
+
+ Py_UCS4 maxchar = PyUnicode_MAX_CHAR_VALUE(ident);
+ if (PyUnicode_MAX_CHAR_VALUE(privateobj) > maxchar) {
+ maxchar = PyUnicode_MAX_CHAR_VALUE(privateobj);
+ }
+
+ PyObject *result = PyUnicode_New(1 + nlen + plen, maxchar);
+ if (!result) {
+ return NULL;
+ }
+ /* ident = "_" + priv[ipriv:] + ident # i.e. 1+plen+nlen bytes */
+ PyUnicode_WRITE(PyUnicode_KIND(result), PyUnicode_DATA(result), 0, '_');
+ if (PyUnicode_CopyCharacters(result, 1, privateobj, ipriv, plen) < 0) {
+ Py_DECREF(result);
+ return NULL;
+ }
+ if (PyUnicode_CopyCharacters(result, plen+1, ident, 0, nlen) < 0) {
+ Py_DECREF(result);
+ return NULL;
+ }
+ assert(_PyUnicode_CheckConsistency(result, 1));
+ return result;
+}