Skip to content

Commit a490662

Browse files
authored
Conformance: broaden enum-expansion tests (#2324)
* Add conformance test for full literal-union equivalency of enums The spec's Enum Literal Expansion section states that a type checker should treat a complete union of all literal members as equivalent to the enum type. Add test to cover cases failing in some checkers. Also refreshes all three quoted spec passages, which had drifted from the section as rewritten in a6eeccf ("new type system concepts section", Reported in #2323. * Update conformance results for the new enum expansion test Four checkers accept the full-literal-union equivalency and are unaffected: mypy, pyrefly, pycroscope and ty (mypy and ty remain "Partial" for the pre-existing `Flag` narrowing gap only). Three drop from "Pass" to "Partial": * pyright and zuban reject both the assignment and the `assert_type`. * basilisk rejects only the assignment; it reports the `assert_type` in test1 as redundant, so it does treat the two types as equivalent there.
1 parent 0dc9b5d commit a490662

9 files changed

Lines changed: 76 additions & 30 deletions

File tree

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
1-
conformant = "Pass"
2-
conformance_automated = "Pass"
1+
conformant = "Partial"
2+
notes = """
3+
Does not accept an enum-typed value where a complete union of all literal members is declared, although it treats the two as equivalent for `assert_type`.
4+
"""
5+
conformance_automated = "Fail"
36
errors_diff = """
7+
Line 85: Unexpected errors ['enums_expansion.py:85:5: error: Type mismatch: `x` is annotated `Literal[Answer.Yes, Answer.No]` (answer.yes | answer.no) but assigned answer [assignment_compatibility]']
48
"""
59
output = """
6-
enums_expansion.py:25:9: error: Redundant `assert_type` with `Literal[Color.GREEN]` on enum-typed parameter [enums_expansion]
7-
enums_expansion.py:53:9: error: Redundant `assert_type` with `Literal[CustomFlags.FLAG3]` on enum-typed parameter [enums_expansion]
10+
enums_expansion.py:28:9: error: Redundant `assert_type` with `Literal[Color.GREEN]` on enum-typed parameter [enums_expansion]
11+
enums_expansion.py:56:9: error: Redundant `assert_type` with `Literal[CustomFlags.FLAG3]` on enum-typed parameter [enums_expansion]
12+
enums_expansion.py:85:5: error: Type mismatch: `x` is annotated `Literal[Answer.Yes, Answer.No]` (answer.yes | answer.no) but assigned answer [assignment_compatibility]
813
"""

conformance/results/mypy/enums_expansion.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ Improperly applies narrowing to `Flag` subclass.
44
"""
55
conformance_automated = "Fail"
66
errors_diff = """
7-
Line 53: Expected 1 errors
8-
Line 52: Unexpected errors ['enums_expansion.py:52: error: Expression is of type "Literal[CustomFlags.FLAG3]", not "CustomFlags" [assert-type]']
7+
Line 56: Expected 1 errors
8+
Line 55: Unexpected errors ['enums_expansion.py:55: error: Expression is of type "Literal[CustomFlags.FLAG3]", not "CustomFlags" [assert-type]']
99
"""
1010
output = """
11-
enums_expansion.py:52: error: Expression is of type "Literal[CustomFlags.FLAG3]", not "CustomFlags" [assert-type]
11+
enums_expansion.py:55: error: Expression is of type "Literal[CustomFlags.FLAG3]", not "CustomFlags" [assert-type]
1212
"""

conformance/results/pycroscope/enums_expansion.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,5 @@ conformance_automated = "Pass"
22
errors_diff = """
33
"""
44
output = """
5-
./enums_expansion.py:53:20: enums_expansion.CustomFlags is not equivalent to Literal[<CustomFlags.FLAG3: 4>]
5+
./enums_expansion.py:56:20: enums_expansion.CustomFlags is not equivalent to Literal[<CustomFlags.FLAG3: 4>]
66
"""

conformance/results/pyrefly/enums_expansion.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,5 @@ conformance_automated = "Pass"
33
errors_diff = """
44
"""
55
output = """
6-
ERROR enums_expansion.py:53:20-51: assert_type(CustomFlags, Literal[CustomFlags.FLAG3]) failed [assert-type]
6+
ERROR enums_expansion.py:56:20-51: assert_type(CustomFlags, Literal[CustomFlags.FLAG3]) failed [assert-type]
77
"""
Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,17 @@
1-
conformant = "Pass"
2-
conformance_automated = "Pass"
1+
conformant = "Partial"
2+
notes = """
3+
Does not treat a complete union of all literal members as equivalent to the enum type.
4+
"""
5+
conformance_automated = "Fail"
36
errors_diff = """
7+
Line 85: Unexpected errors ['enums_expansion.py:85:41 - error: Type "Answer" is not assignable to declared type "Literal[Answer.Yes, Answer.No]"']
8+
Line 86: Unexpected errors ['enums_expansion.py:86:17 - error: "assert_type" mismatch: expected "Literal[Answer.Yes, Answer.No]" but received "Answer" (reportAssertTypeFailure)']
49
"""
510
output = """
6-
enums_expansion.py:53:21 - error: "assert_type" mismatch: expected "Literal[CustomFlags.FLAG3]" but received "CustomFlags" (reportAssertTypeFailure)
11+
enums_expansion.py:56:21 - error: "assert_type" mismatch: expected "Literal[CustomFlags.FLAG3]" but received "CustomFlags" (reportAssertTypeFailure)
12+
enums_expansion.py:85:41 - error: Type "Answer" is not assignable to declared type "Literal[Answer.Yes, Answer.No]"
13+
  Type "Answer" is not assignable to type "Literal[Answer.Yes, Answer.No]"
14+
    "Answer" is not assignable to type "Literal[Answer.Yes]"
15+
    "Answer" is not assignable to type "Literal[Answer.No]" (reportAssignmentType)
16+
enums_expansion.py:86:17 - error: "assert_type" mismatch: expected "Literal[Answer.Yes, Answer.No]" but received "Answer" (reportAssertTypeFailure)
717
"""

conformance/results/results.html

Lines changed: 24 additions & 9 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

conformance/results/ty/enums_expansion.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,5 @@ conformance_automated = "Pass"
22
errors_diff = """
33
"""
44
output = """
5-
enums_expansion.py:53:9: error[type-assertion-failure] Type `CustomFlags & ~Literal[CustomFlags.FLAG1] & ~Literal[CustomFlags.FLAG2]` does not match asserted type `Literal[CustomFlags.FLAG3]`
5+
enums_expansion.py:56:9: error[type-assertion-failure] Type `CustomFlags & ~Literal[CustomFlags.FLAG1] & ~Literal[CustomFlags.FLAG2]` does not match asserted type `Literal[CustomFlags.FLAG3]`
66
"""
Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,15 @@
1-
conformance_automated = "Pass"
1+
conformant = "Partial"
2+
notes = """
3+
Does not treat a complete union of all literal members as equivalent to the enum type.
4+
"""
5+
conformance_automated = "Fail"
26
errors_diff = """
7+
Line 85: Unexpected errors ['enums_expansion.py:85: error: Incompatible types in assignment (expression has type "Answer", variable has type "Literal[Answer.Yes, Answer.No]") [assignment]']
8+
Line 86: Unexpected errors ['enums_expansion.py:86: error: Expression is of type "Answer", not "Literal[Answer.Yes, Answer.No]" [misc]']
39
"""
410
output = """
5-
enums_expansion.py:35: error: Statement is unreachable [unreachable]
6-
enums_expansion.py:53: error: Expression is of type "CustomFlags", not "Literal[CustomFlags.FLAG3]" [misc]
11+
enums_expansion.py:38: error: Statement is unreachable [unreachable]
12+
enums_expansion.py:56: error: Expression is of type "CustomFlags", not "Literal[CustomFlags.FLAG3]" [misc]
13+
enums_expansion.py:85: error: Incompatible types in assignment (expression has type "Answer", variable has type "Literal[Answer.Yes, Answer.No]") [assignment]
14+
enums_expansion.py:86: error: Expression is of type "Answer", not "Literal[Answer.Yes, Answer.No]" [misc]
715
"""

conformance/tests/enums_expansion.py

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,11 @@
88
from typing import Literal, Never, assert_type
99

1010
# > From the perspective of the type system, most enum classes are equivalent
11-
# > to the union of the literal members within that enum. Type checkers may
12-
# > therefore expand an enum type
11+
# > to the union of the literal members within that enum. [...] Because of the
12+
# > equivalency between an enum class and the union of literal members within
13+
# > that enum, the two types may be used interchangeably. Type checkers may
14+
# > therefore expand an enum type (that does not derive from enum.Flag) into a
15+
# > union of literal values during type narrowing and exhaustion detection
1316

1417

1518
class Color(Enum):
@@ -35,8 +38,8 @@ def print_color2(c: Color):
3538
assert_type(c, Never) # E?
3639

3740

38-
# > This rule does not apply to classes that derive from enum. Flag because
39-
# > these enums allow flags to be combined in arbitrary ways.
41+
# > (This rule does not apply to classes that derive from enum.Flag because
42+
# > these enums allow flags to be combined in arbitrary ways.)
4043

4144

4245
class CustomFlags(Flag):
@@ -64,7 +67,7 @@ def test2(f: CustomFlags):
6467

6568

6669
# > A type checker should treat a complete union of all literal members as
67-
# > compatible with the enum type.
70+
# > equivalent to the enum type.
6871

6972

7073
class Answer(Enum):
@@ -76,3 +79,8 @@ def test3(val: object) -> list[Answer]:
7679
assert val is Answer.Yes or val is Answer.No
7780
x = [val]
7881
return x
82+
83+
84+
def test4(a: Answer) -> None:
85+
x: Literal[Answer.Yes, Answer.No] = a
86+
assert_type(a, Literal[Answer.Yes, Answer.No])

0 commit comments

Comments
 (0)