@@ -1116,15 +1116,18 @@ def covers_at_runtime(item: Type, supertype: Type, ignore_promotions: bool) -> b
1116
1116
return False
1117
1117
1118
1118
1119
- def is_proper_subtype (left : Type , right : Type , * , ignore_promotions : bool = False ,
1120
- erase_instances : bool = False ) -> bool :
1119
+ def is_proper_subtype (left : Type , right : Type , * ,
1120
+ ignore_promotions : bool = False ,
1121
+ erase_instances : bool = False ,
1122
+ keep_erased_types : bool = False ) -> bool :
1121
1123
"""Is left a proper subtype of right?
1122
1124
1123
1125
For proper subtypes, there's no need to rely on compatibility due to
1124
1126
Any types. Every usable type is a proper subtype of itself.
1125
1127
1126
1128
If erase_instances is True, erase left instance *after* mapping it to supertype
1127
- (this is useful for runtime isinstance() checks).
1129
+ (this is useful for runtime isinstance() checks). If keep_erased_types is True,
1130
+ do not consider ErasedType a subtype of all types (used by type inference against unions).
1128
1131
"""
1129
1132
if TypeState .is_assumed_proper_subtype (left , right ):
1130
1133
return True
@@ -1135,50 +1138,63 @@ def is_proper_subtype(left: Type, right: Type, *, ignore_promotions: bool = Fals
1135
1138
with pop_on_exit (TypeState ._assuming_proper , left , right ):
1136
1139
return _is_proper_subtype (left , right ,
1137
1140
ignore_promotions = ignore_promotions ,
1138
- erase_instances = erase_instances )
1141
+ erase_instances = erase_instances ,
1142
+ keep_erased_types = keep_erased_types )
1139
1143
return _is_proper_subtype (left , right ,
1140
1144
ignore_promotions = ignore_promotions ,
1141
- erase_instances = erase_instances )
1145
+ erase_instances = erase_instances ,
1146
+ keep_erased_types = keep_erased_types )
1142
1147
1143
1148
1144
- def _is_proper_subtype (left : Type , right : Type , * , ignore_promotions : bool = False ,
1145
- erase_instances : bool = False ) -> bool :
1149
+ def _is_proper_subtype (left : Type , right : Type , * ,
1150
+ ignore_promotions : bool = False ,
1151
+ erase_instances : bool = False ,
1152
+ keep_erased_types : bool = False ) -> bool :
1146
1153
orig_left = left
1147
1154
orig_right = right
1148
1155
left = get_proper_type (left )
1149
1156
right = get_proper_type (right )
1150
1157
1151
1158
if isinstance (right , UnionType ) and not isinstance (left , UnionType ):
1152
- return any ([is_proper_subtype (orig_left , item , ignore_promotions = ignore_promotions ,
1153
- erase_instances = erase_instances )
1159
+ return any ([is_proper_subtype (orig_left , item ,
1160
+ ignore_promotions = ignore_promotions ,
1161
+ erase_instances = erase_instances ,
1162
+ keep_erased_types = keep_erased_types )
1154
1163
for item in right .items ])
1155
1164
return left .accept (ProperSubtypeVisitor (orig_right ,
1156
1165
ignore_promotions = ignore_promotions ,
1157
- erase_instances = erase_instances ))
1166
+ erase_instances = erase_instances ,
1167
+ keep_erased_types = keep_erased_types ))
1158
1168
1159
1169
1160
1170
class ProperSubtypeVisitor (TypeVisitor [bool ]):
1161
1171
def __init__ (self , right : Type , * ,
1162
1172
ignore_promotions : bool = False ,
1163
- erase_instances : bool = False ) -> None :
1173
+ erase_instances : bool = False ,
1174
+ keep_erased_types : bool = False ) -> None :
1164
1175
self .right = get_proper_type (right )
1165
1176
self .orig_right = right
1166
1177
self .ignore_promotions = ignore_promotions
1167
1178
self .erase_instances = erase_instances
1179
+ self .keep_erased_types = keep_erased_types
1168
1180
self ._subtype_kind = ProperSubtypeVisitor .build_subtype_kind (
1169
1181
ignore_promotions = ignore_promotions ,
1170
1182
erase_instances = erase_instances ,
1183
+ keep_erased_types = keep_erased_types
1171
1184
)
1172
1185
1173
1186
@staticmethod
1174
- def build_subtype_kind (* , ignore_promotions : bool = False ,
1175
- erase_instances : bool = False ) -> SubtypeKind :
1176
- return True , ignore_promotions , erase_instances
1187
+ def build_subtype_kind (* ,
1188
+ ignore_promotions : bool = False ,
1189
+ erase_instances : bool = False ,
1190
+ keep_erased_types : bool = False ) -> SubtypeKind :
1191
+ return True , ignore_promotions , erase_instances , keep_erased_types
1177
1192
1178
1193
def _is_proper_subtype (self , left : Type , right : Type ) -> bool :
1179
1194
return is_proper_subtype (left , right ,
1180
1195
ignore_promotions = self .ignore_promotions ,
1181
- erase_instances = self .erase_instances )
1196
+ erase_instances = self .erase_instances ,
1197
+ keep_erased_types = self .keep_erased_types )
1182
1198
1183
1199
def visit_unbound_type (self , left : UnboundType ) -> bool :
1184
1200
# This can be called if there is a bad type annotation. The result probably
@@ -1201,6 +1217,9 @@ def visit_uninhabited_type(self, left: UninhabitedType) -> bool:
1201
1217
def visit_erased_type (self , left : ErasedType ) -> bool :
1202
1218
# This may be encountered during type inference. The result probably doesn't
1203
1219
# matter much.
1220
+ # TODO: it actually does matter, figure out more principled logic about this.
1221
+ if self .keep_erased_types :
1222
+ return False
1204
1223
return True
1205
1224
1206
1225
def visit_deleted_type (self , left : DeletedType ) -> bool :
0 commit comments