summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJelle Zijlstra <jelle.zijlstra@gmail.com>2024-06-17 15:01:49 (GMT)
committerGitHub <noreply@github.com>2024-06-17 15:01:49 (GMT)
commit7c47f93dff878bdc43f5162dd878cbb375711570 (patch)
tree7b02b412d2bc0f718d9df2912fa0b6cd66205de1
parent03b89e3a3d155a06827f58d51238a731d8800cc9 (diff)
downloadcpython-7c47f93dff878bdc43f5162dd878cbb375711570.zip
cpython-7c47f93dff878bdc43f5162dd878cbb375711570.tar.gz
cpython-7c47f93dff878bdc43f5162dd878cbb375711570.tar.bz2
[3.13] gh-119933: Improve ``SyntaxError`` message for invalid type parameters expressions (GH-119976) (#120641)
(cherry picked from commit 4bf17c381fb7b465f0f26aecb94a6c54cf9be2d3) Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com>
-rw-r--r--Doc/library/symtable.rst61
-rw-r--r--Include/internal/pycore_symtable.h31
-rw-r--r--Lib/symtable.py35
-rw-r--r--Lib/test/test_symtable.py12
-rw-r--r--Lib/test/test_syntax.py100
-rw-r--r--Misc/NEWS.d/next/Core and Builtins/2024-06-03-13-48-44.gh-issue-119933.Kc0HG5.rst4
-rw-r--r--Misc/NEWS.d/next/Library/2024-06-05-11-39-21.gh-issue-119933.ooJXQV.rst3
-rw-r--r--Modules/symtablemodule.c6
-rw-r--r--Python/symtable.c80
9 files changed, 277 insertions, 55 deletions
diff --git a/Doc/library/symtable.rst b/Doc/library/symtable.rst
index 0480502..cc10265 100644
--- a/Doc/library/symtable.rst
+++ b/Doc/library/symtable.rst
@@ -31,21 +31,74 @@ Generating Symbol Tables
Examining Symbol Tables
-----------------------
+.. class:: SymbolTableType
+
+ An enumeration indicating the type of a :class:`SymbolTable` object.
+
+ .. attribute:: MODULE
+ :value: "module"
+
+ Used for the symbol table of a module.
+
+ .. attribute:: FUNCTION
+ :value: "function"
+
+ Used for the symbol table of a function.
+
+ .. attribute:: CLASS
+ :value: "class"
+
+ Used for the symbol table of a class.
+
+ The following members refer to different flavors of
+ :ref:`annotation scopes <annotation-scopes>`.
+
+ .. attribute:: ANNOTATION
+ :value: "annotation"
+
+ Used for annotations if ``from __future__ import annotations`` is active.
+
+ .. attribute:: TYPE_ALIAS
+ :value: "type alias"
+
+ Used for the symbol table of :keyword:`type` constructions.
+
+ .. attribute:: TYPE_PARAMETERS
+ :value: "type parameters"
+
+ Used for the symbol table of :ref:`generic functions <generic-functions>`
+ or :ref:`generic classes <generic-classes>`.
+
+ .. attribute:: TYPE_VARIABLE
+ :value: "type variable"
+
+ Used for the symbol table of the bound, the constraint tuple or the
+ default value of a single type variable in the formal sense, i.e.,
+ a TypeVar, a TypeVarTuple or a ParamSpec object (the latter two do
+ not support a bound or a constraint tuple).
+
+ .. versionadded:: 3.13
+
.. class:: SymbolTable
A namespace table for a block. The constructor is not public.
.. method:: get_type()
- Return the type of the symbol table. Possible values are ``'class'``,
- ``'module'``, ``'function'``, ``'annotation'``, ``'TypeVar bound'``,
- ``'type alias'``, and ``'type parameter'``. The latter four refer to
- different flavors of :ref:`annotation scopes <annotation-scopes>`.
+ Return the type of the symbol table. Possible values are members
+ of the :class:`SymbolTableType` enumeration.
.. versionchanged:: 3.12
Added ``'annotation'``, ``'TypeVar bound'``, ``'type alias'``,
and ``'type parameter'`` as possible return values.
+ .. versionchanged:: 3.13
+ Return values are members of the :class:`SymbolTableType` enumeration.
+
+ The exact values of the returned string may change in the future,
+ and thus, it is recommended to use :class:`SymbolTableType` members
+ instead of hard-coded strings.
+
.. method:: get_id()
Return the table's identifier.
diff --git a/Include/internal/pycore_symtable.h b/Include/internal/pycore_symtable.h
index ac6c499..90252bf 100644
--- a/Include/internal/pycore_symtable.h
+++ b/Include/internal/pycore_symtable.h
@@ -15,11 +15,23 @@ typedef enum _block_type {
// Used for annotations if 'from __future__ import annotations' is active.
// Annotation blocks cannot bind names and are not evaluated.
AnnotationBlock,
- // Used for generics and type aliases. These work mostly like functions
- // (see PEP 695 for details). The three different blocks function identically;
- // they are different enum entries only so that error messages can be more
- // precise.
- TypeVarBoundBlock, TypeAliasBlock, TypeParamBlock
+
+ // The following blocks are used for generics and type aliases. These work
+ // mostly like functions (see PEP 695 for details). The three different
+ // blocks function identically; they are different enum entries only so
+ // that error messages can be more precise.
+
+ // The block to enter when processing a "type" (PEP 695) construction,
+ // e.g., "type MyGeneric[T] = list[T]".
+ TypeAliasBlock,
+ // The block to enter when processing a "generic" (PEP 695) object,
+ // e.g., "def foo[T](): pass" or "class A[T]: pass".
+ TypeParametersBlock,
+ // The block to enter when processing the bound, the constraint tuple
+ // or the default value of a single "type variable" in the formal sense,
+ // i.e., a TypeVar, a TypeVarTuple or a ParamSpec object (the latter two
+ // do not support a bound or a constraint tuple).
+ TypeVariableBlock,
} _Py_block_ty;
typedef enum _comprehension_type {
@@ -82,7 +94,16 @@ typedef struct _symtable_entry {
PyObject *ste_children; /* list of child blocks */
PyObject *ste_directives;/* locations of global and nonlocal statements */
PyObject *ste_mangled_names; /* set of names for which mangling should be applied */
+
_Py_block_ty ste_type;
+ // Optional string set by symtable.c and used when reporting errors.
+ // The content of that string is a description of the current "context".
+ //
+ // For instance, if we are processing the default value of the type
+ // variable "T" in "def foo[T = int](): pass", `ste_scope_info` is
+ // set to "a TypeVar default".
+ const char *ste_scope_info;
+
int ste_nested; /* true if block is nested */
unsigned ste_free : 1; /* true if block has free variables */
unsigned ste_child_free : 1; /* true if a child block has free vars,
diff --git a/Lib/symtable.py b/Lib/symtable.py
index 17f820a..500a990 100644
--- a/Lib/symtable.py
+++ b/Lib/symtable.py
@@ -6,8 +6,9 @@ from _symtable import (USE, DEF_GLOBAL, DEF_NONLOCAL, DEF_LOCAL, DEF_PARAM,
LOCAL, GLOBAL_IMPLICIT, GLOBAL_EXPLICIT, CELL)
import weakref
+from enum import StrEnum
-__all__ = ["symtable", "SymbolTable", "Class", "Function", "Symbol"]
+__all__ = ["symtable", "SymbolTableType", "SymbolTable", "Class", "Function", "Symbol"]
def symtable(code, filename, compile_type):
""" Return the toplevel *SymbolTable* for the source code.
@@ -39,6 +40,16 @@ class SymbolTableFactory:
_newSymbolTable = SymbolTableFactory()
+class SymbolTableType(StrEnum):
+ MODULE = "module"
+ FUNCTION = "function"
+ CLASS = "class"
+ ANNOTATION = "annotation"
+ TYPE_ALIAS = "type alias"
+ TYPE_PARAMETERS = "type parameters"
+ TYPE_VARIABLE = "type variable"
+
+
class SymbolTable:
def __init__(self, raw_table, filename):
@@ -62,23 +73,23 @@ class SymbolTable:
def get_type(self):
"""Return the type of the symbol table.
- The values returned are 'class', 'module', 'function',
- 'annotation', 'TypeVar bound', 'type alias', and 'type parameter'.
+ The value returned is one of the values in
+ the ``SymbolTableType`` enumeration.
"""
if self._table.type == _symtable.TYPE_MODULE:
- return "module"
+ return SymbolTableType.MODULE
if self._table.type == _symtable.TYPE_FUNCTION:
- return "function"
+ return SymbolTableType.FUNCTION
if self._table.type == _symtable.TYPE_CLASS:
- return "class"
+ return SymbolTableType.CLASS
if self._table.type == _symtable.TYPE_ANNOTATION:
- return "annotation"
- if self._table.type == _symtable.TYPE_TYPE_VAR_BOUND:
- return "TypeVar bound"
+ return SymbolTableType.ANNOTATION
if self._table.type == _symtable.TYPE_TYPE_ALIAS:
- return "type alias"
- if self._table.type == _symtable.TYPE_TYPE_PARAM:
- return "type parameter"
+ return SymbolTableType.TYPE_ALIAS
+ if self._table.type == _symtable.TYPE_TYPE_PARAMETERS:
+ return SymbolTableType.TYPE_PARAMETERS
+ if self._table.type == _symtable.TYPE_TYPE_VARIABLE:
+ return SymbolTableType.TYPE_VARIABLE
assert False, f"unexpected type: {self._table.type}"
def get_id(self):
diff --git a/Lib/test/test_symtable.py b/Lib/test/test_symtable.py
index 92b78a8..ebd6094 100644
--- a/Lib/test/test_symtable.py
+++ b/Lib/test/test_symtable.py
@@ -49,7 +49,7 @@ type GenericAlias[T] = list[T]
def generic_spam[T](a):
pass
-class GenericMine[T: int]:
+class GenericMine[T: int, U: (int, str) = int]:
pass
"""
@@ -78,6 +78,7 @@ class SymtableTest(unittest.TestCase):
GenericMine = find_block(top, "GenericMine")
GenericMine_inner = find_block(GenericMine, "GenericMine")
T = find_block(GenericMine, "T")
+ U = find_block(GenericMine, "U")
def test_type(self):
self.assertEqual(self.top.get_type(), "module")
@@ -87,13 +88,14 @@ class SymtableTest(unittest.TestCase):
self.assertEqual(self.internal.get_type(), "function")
self.assertEqual(self.foo.get_type(), "function")
self.assertEqual(self.Alias.get_type(), "type alias")
- self.assertEqual(self.GenericAlias.get_type(), "type parameter")
+ self.assertEqual(self.GenericAlias.get_type(), "type parameters")
self.assertEqual(self.GenericAlias_inner.get_type(), "type alias")
- self.assertEqual(self.generic_spam.get_type(), "type parameter")
+ self.assertEqual(self.generic_spam.get_type(), "type parameters")
self.assertEqual(self.generic_spam_inner.get_type(), "function")
- self.assertEqual(self.GenericMine.get_type(), "type parameter")
+ self.assertEqual(self.GenericMine.get_type(), "type parameters")
self.assertEqual(self.GenericMine_inner.get_type(), "class")
- self.assertEqual(self.T.get_type(), "TypeVar bound")
+ self.assertEqual(self.T.get_type(), "type variable")
+ self.assertEqual(self.U.get_type(), "type variable")
def test_id(self):
self.assertGreater(self.top.get_id(), 0)
diff --git a/Lib/test/test_syntax.py b/Lib/test/test_syntax.py
index 491f7fd..cdeb26a 100644
--- a/Lib/test/test_syntax.py
+++ b/Lib/test/test_syntax.py
@@ -2046,16 +2046,91 @@ Invalid expressions in type scopes:
...
SyntaxError: Type parameter list cannot be empty
+ >>> def f[T: (x:=3)](): pass
+ Traceback (most recent call last):
+ ...
+ SyntaxError: named expression cannot be used within a TypeVar bound
+
+ >>> def f[T: ((x:= 3), int)](): pass
+ Traceback (most recent call last):
+ ...
+ SyntaxError: named expression cannot be used within a TypeVar constraint
+
+ >>> def f[T = ((x:=3))](): pass
+ Traceback (most recent call last):
+ ...
+ SyntaxError: named expression cannot be used within a TypeVar default
+
+ >>> async def f[T: (x:=3)](): pass
+ Traceback (most recent call last):
+ ...
+ SyntaxError: named expression cannot be used within a TypeVar bound
+
+ >>> async def f[T: ((x:= 3), int)](): pass
+ Traceback (most recent call last):
+ ...
+ SyntaxError: named expression cannot be used within a TypeVar constraint
+
+ >>> async def f[T = ((x:=3))](): pass
+ Traceback (most recent call last):
+ ...
+ SyntaxError: named expression cannot be used within a TypeVar default
+
>>> type A[T: (x:=3)] = int
Traceback (most recent call last):
...
SyntaxError: named expression cannot be used within a TypeVar bound
+ >>> type A[T: ((x:= 3), int)] = int
+ Traceback (most recent call last):
+ ...
+ SyntaxError: named expression cannot be used within a TypeVar constraint
+
+ >>> type A[T = ((x:=3))] = int
+ Traceback (most recent call last):
+ ...
+ SyntaxError: named expression cannot be used within a TypeVar default
+
+ >>> def f[T: (yield)](): pass
+ Traceback (most recent call last):
+ ...
+ SyntaxError: yield expression cannot be used within a TypeVar bound
+
+ >>> def f[T: (int, (yield))](): pass
+ Traceback (most recent call last):
+ ...
+ SyntaxError: yield expression cannot be used within a TypeVar constraint
+
+ >>> def f[T = (yield)](): pass
+ Traceback (most recent call last):
+ ...
+ SyntaxError: yield expression cannot be used within a TypeVar default
+
+ >>> def f[*Ts = (yield)](): pass
+ Traceback (most recent call last):
+ ...
+ SyntaxError: yield expression cannot be used within a TypeVarTuple default
+
+ >>> def f[**P = [(yield), int]](): pass
+ Traceback (most recent call last):
+ ...
+ SyntaxError: yield expression cannot be used within a ParamSpec default
+
>>> type A[T: (yield 3)] = int
Traceback (most recent call last):
...
SyntaxError: yield expression cannot be used within a TypeVar bound
+ >>> type A[T: (int, (yield 3))] = int
+ Traceback (most recent call last):
+ ...
+ SyntaxError: yield expression cannot be used within a TypeVar constraint
+
+ >>> type A[T = (yield 3)] = int
+ Traceback (most recent call last):
+ ...
+ SyntaxError: yield expression cannot be used within a TypeVar default
+
>>> type A[T: (await 3)] = int
Traceback (most recent call last):
...
@@ -2066,6 +2141,31 @@ Invalid expressions in type scopes:
...
SyntaxError: yield expression cannot be used within a TypeVar bound
+ >>> class A[T: (yield 3)]: pass
+ Traceback (most recent call last):
+ ...
+ SyntaxError: yield expression cannot be used within a TypeVar bound
+
+ >>> class A[T: (int, (yield 3))]: pass
+ Traceback (most recent call last):
+ ...
+ SyntaxError: yield expression cannot be used within a TypeVar constraint
+
+ >>> class A[T = (yield)]: pass
+ Traceback (most recent call last):
+ ...
+ SyntaxError: yield expression cannot be used within a TypeVar default
+
+ >>> class A[*Ts = (yield)]: pass
+ Traceback (most recent call last):
+ ...
+ SyntaxError: yield expression cannot be used within a TypeVarTuple default
+
+ >>> class A[**P = [(yield), int]]: pass
+ Traceback (most recent call last):
+ ...
+ SyntaxError: yield expression cannot be used within a ParamSpec default
+
>>> type A = (x := 3)
Traceback (most recent call last):
...
diff --git a/Misc/NEWS.d/next/Core and Builtins/2024-06-03-13-48-44.gh-issue-119933.Kc0HG5.rst b/Misc/NEWS.d/next/Core and Builtins/2024-06-03-13-48-44.gh-issue-119933.Kc0HG5.rst
new file mode 100644
index 0000000..513a020
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2024-06-03-13-48-44.gh-issue-119933.Kc0HG5.rst
@@ -0,0 +1,4 @@
+Improve :exc:`SyntaxError` messages for invalid expressions in a type
+parameters bound, a type parameter constraint tuple or a default type
+parameter.
+Patch by Bénédikt Tran.
diff --git a/Misc/NEWS.d/next/Library/2024-06-05-11-39-21.gh-issue-119933.ooJXQV.rst b/Misc/NEWS.d/next/Library/2024-06-05-11-39-21.gh-issue-119933.ooJXQV.rst
new file mode 100644
index 0000000..475da88
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2024-06-05-11-39-21.gh-issue-119933.ooJXQV.rst
@@ -0,0 +1,3 @@
+Add the :class:`symtable.SymbolTableType` enumeration to represent the
+possible outputs of the :class:`symtable.SymbolTable.get_type` method. Patch
+by Bénédikt Tran.
diff --git a/Modules/symtablemodule.c b/Modules/symtablemodule.c
index b4dbb54..00e650a 100644
--- a/Modules/symtablemodule.c
+++ b/Modules/symtablemodule.c
@@ -88,11 +88,11 @@ symtable_init_constants(PyObject *m)
return -1;
if (PyModule_AddIntConstant(m, "TYPE_ANNOTATION", AnnotationBlock) < 0)
return -1;
- if (PyModule_AddIntConstant(m, "TYPE_TYPE_VAR_BOUND", TypeVarBoundBlock) < 0)
- return -1;
if (PyModule_AddIntConstant(m, "TYPE_TYPE_ALIAS", TypeAliasBlock) < 0)
return -1;
- if (PyModule_AddIntConstant(m, "TYPE_TYPE_PARAM", TypeParamBlock) < 0)
+ if (PyModule_AddIntConstant(m, "TYPE_TYPE_PARAMETERS", TypeParametersBlock) < 0)
+ return -1;
+ if (PyModule_AddIntConstant(m, "TYPE_TYPE_VARIABLE", TypeVariableBlock) < 0)
return -1;
if (PyModule_AddIntMacro(m, LOCAL) < 0) return -1;
diff --git a/Python/symtable.c b/Python/symtable.c
index 35c1e4a..65dcf67 100644
--- a/Python/symtable.c
+++ b/Python/symtable.c
@@ -58,13 +58,13 @@
#define ANNOTATION_NOT_ALLOWED \
"%s cannot be used within an annotation"
-#define TYPEVAR_BOUND_NOT_ALLOWED \
-"%s cannot be used within a TypeVar bound"
+#define EXPR_NOT_ALLOWED_IN_TYPE_VARIABLE \
+"%s cannot be used within %s"
-#define TYPEALIAS_NOT_ALLOWED \
+#define EXPR_NOT_ALLOWED_IN_TYPE_ALIAS \
"%s cannot be used within a type alias"
-#define TYPEPARAM_NOT_ALLOWED \
+#define EXPR_NOT_ALLOWED_IN_TYPE_PARAMETERS \
"%s cannot be used within the definition of a generic"
#define DUPLICATE_TYPE_PARAM \
@@ -106,6 +106,8 @@ ste_new(struct symtable *st, identifier name, _Py_block_ty block,
ste->ste_mangled_names = NULL;
ste->ste_type = block;
+ ste->ste_scope_info = NULL;
+
ste->ste_nested = 0;
ste->ste_free = 0;
ste->ste_varargs = 0;
@@ -265,9 +267,9 @@ static void _dump_symtable(PySTEntryObject* ste, PyObject* prefix)
case ClassBlock: blocktype = "ClassBlock"; break;
case ModuleBlock: blocktype = "ModuleBlock"; break;
case AnnotationBlock: blocktype = "AnnotationBlock"; break;
- case TypeVarBoundBlock: blocktype = "TypeVarBoundBlock"; break;
+ case TypeVariableBlock: blocktype = "TypeVariableBlock"; break;
case TypeAliasBlock: blocktype = "TypeAliasBlock"; break;
- case TypeParamBlock: blocktype = "TypeParamBlock"; break;
+ case TypeParametersBlock: blocktype = "TypeParametersBlock"; break;
}
const char *comptype = "";
switch (ste->ste_comprehension) {
@@ -525,9 +527,9 @@ int
_PyST_IsFunctionLike(PySTEntryObject *ste)
{
return ste->ste_type == FunctionBlock
- || ste->ste_type == TypeVarBoundBlock
+ || ste->ste_type == TypeVariableBlock
|| ste->ste_type == TypeAliasBlock
- || ste->ste_type == TypeParamBlock;
+ || ste->ste_type == TypeParametersBlock;
}
static int
@@ -1494,7 +1496,7 @@ symtable_enter_type_param_block(struct symtable *st, identifier name,
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,
+ if(!symtable_enter_block(st, name, TypeParametersBlock, ast, lineno,
col_offset, end_lineno, end_col_offset)) {
return 0;
}
@@ -2069,20 +2071,20 @@ symtable_extend_namedexpr_scope(struct symtable *st, expr_ty e)
}
/* Disallow usage in ClassBlock and type scopes */
if (ste->ste_type == ClassBlock ||
- ste->ste_type == TypeParamBlock ||
+ ste->ste_type == TypeParametersBlock ||
ste->ste_type == TypeAliasBlock ||
- ste->ste_type == TypeVarBoundBlock) {
+ ste->ste_type == TypeVariableBlock) {
switch (ste->ste_type) {
case ClassBlock:
PyErr_Format(PyExc_SyntaxError, NAMED_EXPR_COMP_IN_CLASS);
break;
- case TypeParamBlock:
+ case TypeParametersBlock:
PyErr_Format(PyExc_SyntaxError, NAMED_EXPR_COMP_IN_TYPEPARAM);
break;
case TypeAliasBlock:
PyErr_Format(PyExc_SyntaxError, NAMED_EXPR_COMP_IN_TYPEALIAS);
break;
- case TypeVarBoundBlock:
+ case TypeVariableBlock:
PyErr_Format(PyExc_SyntaxError, NAMED_EXPR_COMP_IN_TYPEVAR_BOUND);
break;
default:
@@ -2288,19 +2290,27 @@ symtable_visit_expr(struct symtable *st, expr_ty e)
}
static int
-symtable_visit_type_param_bound_or_default(struct symtable *st, expr_ty e, identifier name, void *key)
+symtable_visit_type_param_bound_or_default(
+ struct symtable *st, expr_ty e, identifier name,
+ void *key, const char *ste_scope_info)
{
if (e) {
int is_in_class = st->st_cur->ste_can_see_class_scope;
- if (!symtable_enter_block(st, name, TypeVarBoundBlock, key, LOCATION(e)))
+ if (!symtable_enter_block(st, name, TypeVariableBlock, key, LOCATION(e)))
return 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(e))) {
VISIT_QUIT(st, 0);
}
+
+ assert(ste_scope_info != NULL);
+ st->st_cur->ste_scope_info = ste_scope_info;
VISIT(st, expr, e);
- if (!symtable_exit_block(st))
+
+ if (!symtable_exit_block(st)) {
return 0;
+ }
}
return 1;
}
@@ -2318,6 +2328,12 @@ symtable_visit_type_param(struct symtable *st, type_param_ty tp)
if (!symtable_add_def(st, tp->v.TypeVar.name, DEF_TYPE_PARAM | DEF_LOCAL, LOCATION(tp)))
VISIT_QUIT(st, 0);
+ const char *ste_scope_info = NULL;
+ const expr_ty bound = tp->v.TypeVar.bound;
+ if (bound != NULL) {
+ ste_scope_info = bound->kind == Tuple_kind ? "a TypeVar constraint" : "a TypeVar bound";
+ }
+
// We must use a different key for the bound and default. The obvious choice would be to
// use the .bound and .default_value pointers, but that fails when the expression immediately
// inside the bound or default is a comprehension: we would reuse the same key for
@@ -2325,11 +2341,12 @@ symtable_visit_type_param(struct symtable *st, type_param_ty tp)
// The only requirement for the key is that it is unique and it matches the logic in
// compile.c where the scope is retrieved.
if (!symtable_visit_type_param_bound_or_default(st, tp->v.TypeVar.bound, tp->v.TypeVar.name,
- (void *)tp)) {
+ (void *)tp, ste_scope_info)) {
VISIT_QUIT(st, 0);
}
+
if (!symtable_visit_type_param_bound_or_default(st, tp->v.TypeVar.default_value, tp->v.TypeVar.name,
- (void *)((uintptr_t)tp + 1))) {
+ (void *)((uintptr_t)tp + 1), "a TypeVar default")) {
VISIT_QUIT(st, 0);
}
break;
@@ -2337,8 +2354,9 @@ symtable_visit_type_param(struct symtable *st, type_param_ty tp)
if (!symtable_add_def(st, tp->v.TypeVarTuple.name, DEF_TYPE_PARAM | DEF_LOCAL, LOCATION(tp))) {
VISIT_QUIT(st, 0);
}
+
if (!symtable_visit_type_param_bound_or_default(st, tp->v.TypeVarTuple.default_value, tp->v.TypeVarTuple.name,
- (void *)tp)) {
+ (void *)tp, "a TypeVarTuple default")) {
VISIT_QUIT(st, 0);
}
break;
@@ -2346,8 +2364,9 @@ symtable_visit_type_param(struct symtable *st, type_param_ty tp)
if (!symtable_add_def(st, tp->v.ParamSpec.name, DEF_TYPE_PARAM | DEF_LOCAL, LOCATION(tp))) {
VISIT_QUIT(st, 0);
}
+
if (!symtable_visit_type_param_bound_or_default(st, tp->v.ParamSpec.default_value, tp->v.ParamSpec.name,
- (void *)tp)) {
+ (void *)tp, "a ParamSpec default")) {
VISIT_QUIT(st, 0);
}
break;
@@ -2729,12 +2748,21 @@ symtable_raise_if_annotation_block(struct symtable *st, const char *name, expr_t
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 if (type == TypeVariableBlock) {
+ const char *info = st->st_cur->ste_scope_info;
+ assert(info != NULL); // e.g., info == "a ParamSpec default"
+ PyErr_Format(PyExc_SyntaxError, EXPR_NOT_ALLOWED_IN_TYPE_VARIABLE, name, info);
+ }
+ else if (type == TypeAliasBlock) {
+ // for now, we do not have any extra information
+ assert(st->st_cur->ste_scope_info == NULL);
+ PyErr_Format(PyExc_SyntaxError, EXPR_NOT_ALLOWED_IN_TYPE_ALIAS, name);
+ }
+ else if (type == TypeParametersBlock) {
+ // for now, we do not have any extra information
+ assert(st->st_cur->ste_scope_info == NULL);
+ PyErr_Format(PyExc_SyntaxError, EXPR_NOT_ALLOWED_IN_TYPE_PARAMETERS, name);
+ }
else
return 1;