Skip to content

Commit e13c690

Browse files
committed
Merge branch 'main' into specialize-non-generic
2 parents b670bf4 + ac882f7 commit e13c690

File tree

3 files changed

+63
-11
lines changed

3 files changed

+63
-11
lines changed

crates/ty_python_semantic/resources/mdtest/generics/legacy/paramspec.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,7 @@ Explicit specialization of a generic class involving `ParamSpec` is done by prov
244244
of types, `...`, or another in-scope `ParamSpec`.
245245

246246
```py
247+
reveal_type(OnlyParamSpec[[]]().attr) # revealed: () -> None
247248
reveal_type(OnlyParamSpec[[int, str]]().attr) # revealed: (int, str, /) -> None
248249
reveal_type(OnlyParamSpec[...]().attr) # revealed: (...) -> None
249250

@@ -252,8 +253,28 @@ def func(c: Callable[P2, None]):
252253

253254
# error: [invalid-type-form] "ParamSpec `P2` is unbound"
254255
reveal_type(OnlyParamSpec[P2]().attr) # revealed: (...) -> None
256+
257+
# error: [invalid-type-arguments] "No type argument provided for required type variable `P1` of class `OnlyParamSpec`"
258+
reveal_type(OnlyParamSpec[()]().attr) # revealed: (...) -> None
259+
```
260+
261+
An explicit tuple expression (unlike an implicit one that omits the parentheses) is also accepted
262+
when the `ParamSpec` is the only type variable. But, this isn't recommended is mainly a fallout of
263+
it having the same AST as the one without the parentheses. Both mypy and Pyright also allow this.
264+
265+
```py
266+
reveal_type(OnlyParamSpec[(int, str)]().attr) # revealed: (int, str, /) -> None
255267
```
256268

269+
<!-- blacken-docs:off -->
270+
271+
```py
272+
# error: [invalid-syntax]
273+
reveal_type(OnlyParamSpec[]().attr) # revealed: (...) -> None
274+
```
275+
276+
<!-- blacken-docs:on -->
277+
257278
The square brackets can be omitted when `ParamSpec` is the only type variable
258279

259280
```py
@@ -269,6 +290,7 @@ reveal_type(OnlyParamSpec[int]().attr) # revealed: (int, /) -> None
269290
But, they cannot be omitted when there are multiple type variables.
270291

271292
```py
293+
reveal_type(TypeVarAndParamSpec[int, []]().attr) # revealed: () -> int
272294
reveal_type(TypeVarAndParamSpec[int, [int, str]]().attr) # revealed: (int, str, /) -> int
273295
reveal_type(TypeVarAndParamSpec[int, [str]]().attr) # revealed: (str, /) -> int
274296
reveal_type(TypeVarAndParamSpec[int, ...]().attr) # revealed: (...) -> int
@@ -277,6 +299,10 @@ reveal_type(TypeVarAndParamSpec[int, ...]().attr) # revealed: (...) -> int
277299
reveal_type(TypeVarAndParamSpec[int, P2]().attr) # revealed: (...) -> int
278300
# error: [invalid-type-arguments] "Type argument for `ParamSpec` must be either a list of types, `ParamSpec`, `Concatenate`, or `...`"
279301
reveal_type(TypeVarAndParamSpec[int, int]().attr) # revealed: (...) -> int
302+
# error: [invalid-type-arguments] "Type argument for `ParamSpec` must be"
303+
reveal_type(TypeVarAndParamSpec[int, ()]().attr) # revealed: (...) -> int
304+
# error: [invalid-type-arguments] "Type argument for `ParamSpec` must be"
305+
reveal_type(TypeVarAndParamSpec[int, (int, str)]().attr) # revealed: (...) -> int
280306
```
281307

282308
Nor can they be omitted when there are more than one `ParamSpec`s.

crates/ty_python_semantic/resources/mdtest/generics/pep695/paramspec.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,7 @@ Explicit specialization of a generic class involving `ParamSpec` is done by prov
228228
of types, `...`, or another in-scope `ParamSpec`.
229229

230230
```py
231+
reveal_type(OnlyParamSpec[[]]().attr) # revealed: () -> None
231232
reveal_type(OnlyParamSpec[[int, str]]().attr) # revealed: (int, str, /) -> None
232233
reveal_type(OnlyParamSpec[...]().attr) # revealed: (...) -> None
233234

@@ -238,8 +239,28 @@ P2 = ParamSpec("P2")
238239

239240
# error: [invalid-type-form] "ParamSpec `P2` is unbound"
240241
reveal_type(OnlyParamSpec[P2]().attr) # revealed: (...) -> None
242+
243+
# error: [invalid-type-arguments] "No type argument provided for required type variable `P1` of class `OnlyParamSpec`"
244+
reveal_type(OnlyParamSpec[()]().attr) # revealed: (...) -> None
241245
```
242246

247+
An explicit tuple expression (unlike an implicit one that omits the parentheses) is also accepted
248+
when the `ParamSpec` is the only type variable. But, this isn't recommended is mainly a fallout of
249+
it having the same AST as the one without the parentheses. Both mypy and Pyright also allow this.
250+
251+
```py
252+
reveal_type(OnlyParamSpec[(int, str)]().attr) # revealed: (int, str, /) -> None
253+
```
254+
255+
<!-- blacken-docs:off -->
256+
257+
```py
258+
# error: [invalid-syntax]
259+
reveal_type(OnlyParamSpec[]().attr) # revealed: (...) -> None
260+
```
261+
262+
<!-- blacken-docs:on -->
263+
243264
The square brackets can be omitted when `ParamSpec` is the only type variable
244265

245266
```py
@@ -255,6 +276,7 @@ reveal_type(OnlyParamSpec[int]().attr) # revealed: (int, /) -> None
255276
But, they cannot be omitted when there are multiple type variables.
256277

257278
```py
279+
reveal_type(TypeVarAndParamSpec[int, []]().attr) # revealed: () -> int
258280
reveal_type(TypeVarAndParamSpec[int, [int, str]]().attr) # revealed: (int, str, /) -> int
259281
reveal_type(TypeVarAndParamSpec[int, [str]]().attr) # revealed: (str, /) -> int
260282
reveal_type(TypeVarAndParamSpec[int, ...]().attr) # revealed: (...) -> int
@@ -263,6 +285,10 @@ reveal_type(TypeVarAndParamSpec[int, ...]().attr) # revealed: (...) -> int
263285
reveal_type(TypeVarAndParamSpec[int, P2]().attr) # revealed: (...) -> int
264286
# error: [invalid-type-arguments]
265287
reveal_type(TypeVarAndParamSpec[int, int]().attr) # revealed: (...) -> int
288+
# error: [invalid-type-arguments] "Type argument for `ParamSpec` must be"
289+
reveal_type(TypeVarAndParamSpec[int, ()]().attr) # revealed: (...) -> int
290+
# error: [invalid-type-arguments] "Type argument for `ParamSpec` must be"
291+
reveal_type(TypeVarAndParamSpec[int, (int, str)]().attr) # revealed: (...) -> int
266292
```
267293

268294
Nor can they be omitted when there are more than one `ParamSpec`.

crates/ty_python_semantic/src/types/infer/builder.rs

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3472,17 +3472,13 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
34723472
));
34733473
}
34743474

3475+
ast::Expr::Tuple(_) if !exactly_one_paramspec => {
3476+
// Tuple expression is only allowed when the generic context contains only one
3477+
// `ParamSpec` type variable and no other type variables.
3478+
}
3479+
34753480
ast::Expr::Tuple(ast::ExprTuple { elts, .. })
34763481
| ast::Expr::List(ast::ExprList { elts, .. }) => {
3477-
// This should be taken care of by the caller.
3478-
if expr.is_tuple_expr() {
3479-
assert!(
3480-
exactly_one_paramspec,
3481-
"Inferring ParamSpec value during explicit specialization for a \
3482-
tuple expression should only happen when it contains exactly one ParamSpec"
3483-
);
3484-
}
3485-
34863482
let mut parameter_types = Vec::with_capacity(elts.len());
34873483

34883484
// Whether to infer `Todo` for the parameters
@@ -3519,7 +3515,11 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
35193515
return Ok(Type::paramspec_value_callable(db, Parameters::todo()));
35203516
}
35213517

3522-
ast::Expr::Name(_) => {
3518+
ast::Expr::Name(name) => {
3519+
if name.is_invalid() {
3520+
return Err(());
3521+
}
3522+
35233523
let param_type = self.infer_type_expression(expr);
35243524

35253525
match param_type {
@@ -11650,7 +11650,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
1165011650
let exactly_one_paramspec = generic_context.exactly_one_paramspec(db);
1165111651
let (type_arguments, store_inferred_type_arguments) = match slice_node {
1165211652
ast::Expr::Tuple(tuple) => {
11653-
if exactly_one_paramspec {
11653+
if exactly_one_paramspec && !tuple.elts.is_empty() {
1165411654
(std::slice::from_ref(slice_node), false)
1165511655
} else {
1165611656
(tuple.elts.as_slice(), true)

0 commit comments

Comments
 (0)