diff --git a/mypy/join.py b/mypy/join.py index 62d256f4440f..ea91916867b9 100644 --- a/mypy/join.py +++ b/mypy/join.py @@ -97,7 +97,7 @@ def join_instances(self, t: Instance, s: Instance) -> ProperType: else: # ParamSpec type variables behave the same, independent of variance if not is_equivalent(ta, sa): - return get_proper_type(type_var.upper_bound) + return object_from_instance(t) new_type = join_types(ta, sa, self) assert new_type is not None args.append(new_type) diff --git a/mypy/semanal.py b/mypy/semanal.py index 70bd876af46e..872ff77a5f1c 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -4183,9 +4183,13 @@ def process_paramspec_declaration(self, s: AssignmentStmt) -> bool: # on the lines of process_typevar_parameters if not call.analyzed: - paramspec_var = ParamSpecExpr( - name, self.qualified_name(name), self.object_type(), INVARIANT + parameters = Parameters( + arg_types=[self.object_type(), self.object_type()], + arg_kinds=[ARG_STAR, ARG_STAR2], + arg_names=[None, None], + is_ellipsis_args=True, ) + paramspec_var = ParamSpecExpr(name, self.qualified_name(name), parameters, INVARIANT) paramspec_var.line = call.line call.analyzed = paramspec_var else: diff --git a/mypy/types.py b/mypy/types.py index f23800234600..a25f89db2f87 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -704,13 +704,14 @@ def copy_modified( id: Bogus[TypeVarId | int] = _dummy, flavor: int = _dummy_int, prefix: Bogus[Parameters] = _dummy, + upper_bound: Bogus[Type] = _dummy, ) -> ParamSpecType: return ParamSpecType( self.name, self.fullname, id if id is not _dummy else self.id, flavor if flavor != _dummy_int else self.flavor, - self.upper_bound, + upper_bound if upper_bound is not _dummy else self.upper_bound, line=self.line, column=self.column, prefix=prefix if prefix is not _dummy else self.prefix, @@ -1991,7 +1992,18 @@ def param_spec(self) -> ParamSpecType | None: # TODO: confirm that all arg kinds are positional prefix = Parameters(self.arg_types[:-2], self.arg_kinds[:-2], self.arg_names[:-2]) - return arg_type.copy_modified(flavor=ParamSpecFlavor.BARE, prefix=prefix) + # TODO: should this take in `object`s? Or use prefix + *Any **Any as upper bound? + any_type = AnyType(TypeOfAny.special_form) + return arg_type.copy_modified( + flavor=ParamSpecFlavor.BARE, + prefix=prefix, + upper_bound=Parameters( + arg_types=[any_type, any_type], + arg_kinds=[ARG_STAR, ARG_STAR2], + arg_names=[None, None], + is_ellipsis_args=True, + ), + ) def expand_param_spec( self, c: CallableType | Parameters, no_prefix: bool = False diff --git a/test-data/unit/semanal-types.test b/test-data/unit/semanal-types.test index 71a5c6dd87b5..d9ef030f034a 100644 --- a/test-data/unit/semanal-types.test +++ b/test-data/unit/semanal-types.test @@ -1549,7 +1549,8 @@ MypyFile:1( ImportFrom:1(typing, [ParamSpec]) AssignmentStmt:2( NameExpr(P* [__main__.P]) - ParamSpecExpr:2())) + ParamSpecExpr:2( + UpperBound(...)))) [case testTypeVarTuple] from typing_extensions import TypeVarTuple