Skip to content

Support typing.NoReturn and typing.Never#14559

Merged
carljm merged 8 commits intoastral-sh:mainfrom
Glyphack:typing-no-return-never
Nov 25, 2024
Merged

Support typing.NoReturn and typing.Never#14559
carljm merged 8 commits intoastral-sh:mainfrom
Glyphack:typing-no-return-never

Conversation

@Glyphack
Copy link
Copy Markdown
Contributor

@Glyphack Glyphack commented Nov 23, 2024

Fix #14558

Summary

  • Add typing.NoReturn and typing.Never to known instances and infer them as Type::Never
  • Add is_assignable_to cases for Type::Never

I skipped emitting diagnostic for when a function is annotated as NoReturn but it actually returns.

Test Plan

Added tests from
https://github.com/python/typing/blob/main/conformance/tests/specialtypes_never.py
except from generics and checking if the return value of the function and the annotations match.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Nov 23, 2024

ruff-ecosystem results

Linter (stable)

✅ ecosystem check detected no linter changes.

Linter (preview)

✅ ecosystem check detected no linter changes.

@AlexWaygood AlexWaygood added the ty Multi-file analysis & type inference label Nov 23, 2024
Comment thread crates/red_knot_python_semantic/src/types.rs Outdated
@Glyphack Glyphack marked this pull request as ready for review November 24, 2024 13:54
Copy link
Copy Markdown
Member

@AlexWaygood AlexWaygood left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! Not a full review yet, but one important thing I noticed:

Comment thread crates/red_knot_python_semantic/src/types/infer.rs
@AlexWaygood AlexWaygood dismissed their stale review November 24, 2024 20:25

panic was fixed

Copy link
Copy Markdown
Contributor

@carljm carljm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also not a full review yet, just a couple things I noticed in passing.

Comment thread crates/red_knot_python_semantic/resources/mdtest/annotations/never.md Outdated
Comment thread crates/red_knot_python_semantic/src/types.rs Outdated
Comment thread crates/red_knot_python_semantic/src/types/infer.rs Outdated
Comment on lines +18 to +16
# revealed: NoReturn
reveal_type(stop())
Copy link
Copy Markdown
Contributor

@sharkdp sharkdp Nov 25, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should be a separate test that ends here. If you were to actually run this code, the program would never return from the stop() call.

Everything after this line can be considered unreachable code from the perspective of static analysis. Which also means we might not (should not) emit any diagnostics, including reveal_type-information. I guess it's even questionable as to whether or not the reveal_type in this line should emit a diagnostic. You would not see any result at runtime. Existing type checkers still reveal the type for the stop() call, but nothing beyond.

Similar reasoning applies to some of the tests below, I think.

Copy link
Copy Markdown
Contributor Author

@Glyphack Glyphack Nov 25, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My intention was to test the revealed type only. You're right this test will break when the behavior for Never is added.
I will move this call and other reveal_types that can stop the type checking to a function.

I think this should be a separate test that ends here.

Are you suggesting to implement this behavior in this PR? Otherwise I can add a todo comment here and work on it in a follow up PR.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you suggesting to implement this behavior in this PR?

Ah, no — sorry. I was merely suggesting that we split this large test into multiple smaller tests. And take care not to have any test assertions (# revealed: / # error: ) in lines following a call to a function that returns Never/NoReturn. To make sure this test will work in the future once we add unreachable-code analysis.

Writing smaller self-consistent tests would also be beneficial if we ever need to debug an assertion failure in this file.

@carljm
Copy link
Copy Markdown
Contributor

carljm commented Nov 25, 2024

I skipped emitting diagnostic for when a function is annotated as NoReturn but it actually returns.

This is correct to skip in this PR; there shouldn't be any special-casing of such a check, it will just fall out naturally from checking return type assignability in general, since nothing (except for Never) is assignable to Never.

@Glyphack Glyphack force-pushed the typing-no-return-never branch from 57c7764 to f8debee Compare November 25, 2024 18:09
Comment thread crates/red_knot_python_semantic/src/types/infer.rs Outdated
@Glyphack Glyphack force-pushed the typing-no-return-never branch from 59d08ba to 7102da0 Compare November 25, 2024 18:32
Copy link
Copy Markdown
Contributor

@carljm carljm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great, thank you! Just a few small tweaks which I'll push, and then merge.

Comment on lines +3 to +5
`NoReturn` to annotate functions that never return normally. `Never` represents the bottom type, a
type that represents the empty set of Python objects. These two annotations can be used
interchangeably.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
`NoReturn` to annotate functions that never return normally. `Never` represents the bottom type, a
type that represents the empty set of Python objects. These two annotations can be used
interchangeably.
`NoReturn` is used to annotate functions that never return (they always raise an exception or exit the process).
`Never` is the bottom type, and represents the empty set of Python objects. These two annotations can be used
interchangeably.

},
)
}
(Type::Never, Type::Never) => false,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this case is a) wrong (because Never should be assignable to Never) and b) never reached, due to the is_equivalent_to check that takes precedence above.

There is a test verifying that Never is actually assignable to Never.

Suggested change
(Type::Never, Type::Never) => false,

)
}
(Type::Never, Type::Never) => false,
(_, Type::Never) => false,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And I think this case should also not be necessary, because we fallback to is_subtype_of, which already contains this same case for Never. Removing this line and the previous one, all tests still pass.

Suggested change
(_, Type::Never) => false,

@carljm carljm force-pushed the typing-no-return-never branch from 1de7286 to a217be4 Compare November 25, 2024 21:33
@carljm carljm enabled auto-merge (squash) November 25, 2024 21:33
@carljm carljm merged commit 557d583 into astral-sh:main Nov 25, 2024
@Glyphack Glyphack deleted the typing-no-return-never branch November 26, 2024 09:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ty Multi-file analysis & type inference

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[red-knot] support typing.NoReturn and typing.Never in annotations

5 participants