Skip to content

Commit d406157

Browse files
committed
Don't call union simplification during semantic analysis
We generally don't have fully constructed TypeInfos so we can't do proper union simplification. Just implement simple-minded simplifaction that deals with the cases we care about.
1 parent 9cf9114 commit d406157

File tree

2 files changed

+35
-3
lines changed

2 files changed

+35
-3
lines changed

mypy/typeanal.py

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ def visit_unbound_type(self, t: UnboundType) -> Type:
109109
t.optional = False
110110
# We don't need to worry about double-wrapping Optionals or
111111
# wrapping Anys: Union simplification will take care of that.
112-
return UnionType.make_simplified_union([self.visit_unbound_type(t), NoneTyp()])
112+
return make_optional_type(self.visit_unbound_type(t))
113113
sym = self.lookup(t.name, t)
114114
if sym is not None:
115115
if sym.node is None:
@@ -151,7 +151,7 @@ def visit_unbound_type(self, t: UnboundType) -> Type:
151151
self.fail('Optional[...] must have exactly one type argument', t)
152152
return AnyType()
153153
item = self.anal_type(t.args[0])
154-
return UnionType.make_simplified_union([item, NoneTyp()])
154+
return make_optional_type(item)
155155
elif fullname == 'typing.Callable':
156156
return self.analyze_callable_type(t)
157157
elif fullname == 'typing.Type':
@@ -557,3 +557,20 @@ def visit_partial_type(self, t: PartialType) -> None:
557557

558558
def visit_type_type(self, t: TypeType) -> None:
559559
pass
560+
561+
562+
def make_optional_type(t: Type) -> Type:
563+
"""Return the type corresponding to Optional[t].
564+
565+
Note that we can't use normal union simplification, since this function
566+
is called during semantic analysis and simplification only works during
567+
type checking.
568+
"""
569+
if not experiments.STRICT_OPTIONAL:
570+
return t
571+
if isinstance(t, NoneTyp):
572+
return t
573+
if isinstance(t, UnionType) and any(isinstance(item, NoneTyp)
574+
for item in t.items):
575+
return t
576+
return UnionType([t, NoneTyp()], t.line, t.column)

mypy/types.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -986,6 +986,21 @@ def make_union(items: List[Type], line: int = -1, column: int = -1) -> Type:
986986

987987
@staticmethod
988988
def make_simplified_union(items: List[Type], line: int = -1, column: int = -1) -> Type:
989+
"""Build union type with redundant union items removed.
990+
991+
If only a single item remains, this may return a non-union type.
992+
993+
Examples:
994+
995+
* [int, str] -> Union[int, str]
996+
* [int, object] -> object
997+
* [int, int] -> int
998+
* [int, Any] -> Union[int, Any] (Any types are not simplified away!)
999+
* [Any, Any] -> Any
1000+
1001+
Note: This must NOT be used during semantic analysis, since TypeInfos may not
1002+
be fully initialized.
1003+
"""
9891004
while any(isinstance(typ, UnionType) for typ in items):
9901005
all_items = [] # type: List[Type]
9911006
for typ in items:
@@ -1708,7 +1723,7 @@ def set_typ_args(tp: Type, new_args: List[Type], line: int = -1, column: int = -
17081723
if isinstance(tp, TupleType):
17091724
return tp.copy_modified(items=new_args)
17101725
if isinstance(tp, UnionType):
1711-
return UnionType.make_simplified_union(new_args, line, column)
1726+
return UnionType(new_args, line, column)
17121727
if isinstance(tp, CallableType):
17131728
return tp.copy_modified(arg_types=new_args[:-1], ret_type=new_args[-1],
17141729
line=line, column=column)

0 commit comments

Comments
 (0)