@@ -97,37 +97,70 @@ impl<'db> UnionElement<'db> {
97
97
fn try_reduce ( & mut self , db : & ' db dyn Db , other_type : Type < ' db > ) -> ReduceResult < ' db > {
98
98
match self {
99
99
UnionElement :: IntLiterals ( literals) => {
100
- ReduceResult :: KeepIf ( if other_type. splits_literals ( db, LiteralKind :: Int ) {
100
+ if other_type. splits_literals ( db, LiteralKind :: Int ) {
101
+ let mut collapse = false ;
102
+ let negated = other_type. negate ( db) ;
101
103
literals. retain ( |literal| {
102
- !Type :: IntLiteral ( * literal) . is_subtype_of ( db, other_type)
104
+ let ty = Type :: IntLiteral ( * literal) ;
105
+ if negated. is_subtype_of ( db, ty) {
106
+ collapse = true ;
107
+ }
108
+ !ty. is_subtype_of ( db, other_type)
103
109
} ) ;
104
- !literals. is_empty ( )
110
+ if collapse {
111
+ ReduceResult :: CollapseToObject
112
+ } else {
113
+ ReduceResult :: KeepIf ( !literals. is_empty ( ) )
114
+ }
105
115
} else {
106
- // SAFETY: All `UnionElement` literal kinds must always be non-empty
107
- !Type :: IntLiteral ( literals[ 0 ] ) . is_subtype_of ( db, other_type)
108
- } )
116
+ ReduceResult :: KeepIf (
117
+ !Type :: IntLiteral ( literals[ 0 ] ) . is_subtype_of ( db, other_type) ,
118
+ )
119
+ }
109
120
}
110
121
UnionElement :: StringLiterals ( literals) => {
111
- ReduceResult :: KeepIf ( if other_type. splits_literals ( db, LiteralKind :: String ) {
122
+ if other_type. splits_literals ( db, LiteralKind :: String ) {
123
+ let mut collapse = false ;
124
+ let negated = other_type. negate ( db) ;
112
125
literals. retain ( |literal| {
113
- !Type :: StringLiteral ( * literal) . is_subtype_of ( db, other_type)
126
+ let ty = Type :: StringLiteral ( * literal) ;
127
+ if negated. is_subtype_of ( db, ty) {
128
+ collapse = true ;
129
+ }
130
+ !ty. is_subtype_of ( db, other_type)
114
131
} ) ;
115
- !literals. is_empty ( )
132
+ if collapse {
133
+ ReduceResult :: CollapseToObject
134
+ } else {
135
+ ReduceResult :: KeepIf ( !literals. is_empty ( ) )
136
+ }
116
137
} else {
117
- // SAFETY: All `UnionElement` literal kinds must always be non-empty
118
- !Type :: StringLiteral ( literals[ 0 ] ) . is_subtype_of ( db, other_type)
119
- } )
138
+ ReduceResult :: KeepIf (
139
+ !Type :: StringLiteral ( literals[ 0 ] ) . is_subtype_of ( db, other_type) ,
140
+ )
141
+ }
120
142
}
121
143
UnionElement :: BytesLiterals ( literals) => {
122
- ReduceResult :: KeepIf ( if other_type. splits_literals ( db, LiteralKind :: Bytes ) {
144
+ if other_type. splits_literals ( db, LiteralKind :: Bytes ) {
145
+ let mut collapse = false ;
146
+ let negated = other_type. negate ( db) ;
123
147
literals. retain ( |literal| {
124
- !Type :: BytesLiteral ( * literal) . is_subtype_of ( db, other_type)
148
+ let ty = Type :: BytesLiteral ( * literal) ;
149
+ if negated. is_subtype_of ( db, ty) {
150
+ collapse = true ;
151
+ }
152
+ !ty. is_subtype_of ( db, other_type)
125
153
} ) ;
126
- !literals. is_empty ( )
154
+ if collapse {
155
+ ReduceResult :: CollapseToObject
156
+ } else {
157
+ ReduceResult :: KeepIf ( !literals. is_empty ( ) )
158
+ }
127
159
} else {
128
- // SAFETY: All `UnionElement` literal kinds must always be non-empty
129
- !Type :: BytesLiteral ( literals[ 0 ] ) . is_subtype_of ( db, other_type)
130
- } )
160
+ ReduceResult :: KeepIf (
161
+ !Type :: BytesLiteral ( literals[ 0 ] ) . is_subtype_of ( db, other_type) ,
162
+ )
163
+ }
131
164
}
132
165
UnionElement :: Type ( existing) => ReduceResult :: Type ( * existing) ,
133
166
}
@@ -138,6 +171,8 @@ enum ReduceResult<'db> {
138
171
/// Reduction of this `UnionElement` is complete; keep it in the union if the nested
139
172
/// boolean is true, eliminate it from the union if false.
140
173
KeepIf ( bool ) ,
174
+ /// Collapse this entire union to `object`.
175
+ CollapseToObject ,
141
176
/// The given `Type` can stand-in for the entire `UnionElement` for further union
142
177
/// simplification checks.
143
178
Type ( Type < ' db > ) ,
@@ -195,6 +230,7 @@ impl<'db> UnionBuilder<'db> {
195
230
// containing it.
196
231
Type :: StringLiteral ( literal) => {
197
232
let mut found = false ;
233
+ let ty_negated = ty. negate ( self . db ) ;
198
234
for element in & mut self . elements {
199
235
match element {
200
236
UnionElement :: StringLiterals ( literals) => {
@@ -207,8 +243,16 @@ impl<'db> UnionBuilder<'db> {
207
243
found = true ;
208
244
break ;
209
245
}
210
- UnionElement :: Type ( existing) if ty. is_subtype_of ( self . db , * existing) => {
211
- return ;
246
+ UnionElement :: Type ( existing) => {
247
+ if ty. is_subtype_of ( self . db , * existing) {
248
+ return ;
249
+ }
250
+ if ty_negated. is_subtype_of ( self . db , * existing) {
251
+ // The type that includes both this new element, and its negation
252
+ // (or a supertype of its negation), must be simply `object`.
253
+ self . collapse_to_object ( ) ;
254
+ return ;
255
+ }
212
256
}
213
257
_ => { }
214
258
}
@@ -223,6 +267,7 @@ impl<'db> UnionBuilder<'db> {
223
267
// Same for bytes literals as for string literals, above.
224
268
Type :: BytesLiteral ( literal) => {
225
269
let mut found = false ;
270
+ let ty_negated = ty. negate ( self . db ) ;
226
271
for element in & mut self . elements {
227
272
match element {
228
273
UnionElement :: BytesLiterals ( literals) => {
@@ -235,8 +280,16 @@ impl<'db> UnionBuilder<'db> {
235
280
found = true ;
236
281
break ;
237
282
}
238
- UnionElement :: Type ( existing) if ty. is_subtype_of ( self . db , * existing) => {
239
- return ;
283
+ UnionElement :: Type ( existing) => {
284
+ if ty. is_subtype_of ( self . db , * existing) {
285
+ return ;
286
+ }
287
+ if ty_negated. is_subtype_of ( self . db , * existing) {
288
+ // The type that includes both this new element, and its negation
289
+ // (or a supertype of its negation), must be simply `object`.
290
+ self . collapse_to_object ( ) ;
291
+ return ;
292
+ }
240
293
}
241
294
_ => { }
242
295
}
@@ -251,6 +304,7 @@ impl<'db> UnionBuilder<'db> {
251
304
// And same for int literals as well.
252
305
Type :: IntLiteral ( literal) => {
253
306
let mut found = false ;
307
+ let ty_negated = ty. negate ( self . db ) ;
254
308
for element in & mut self . elements {
255
309
match element {
256
310
UnionElement :: IntLiterals ( literals) => {
@@ -263,8 +317,16 @@ impl<'db> UnionBuilder<'db> {
263
317
found = true ;
264
318
break ;
265
319
}
266
- UnionElement :: Type ( existing) if ty. is_subtype_of ( self . db , * existing) => {
267
- return ;
320
+ UnionElement :: Type ( existing) => {
321
+ if ty. is_subtype_of ( self . db , * existing) {
322
+ return ;
323
+ }
324
+ if ty_negated. is_subtype_of ( self . db , * existing) {
325
+ // The type that includes both this new element, and its negation
326
+ // (or a supertype of its negation), must be simply `object`.
327
+ self . collapse_to_object ( ) ;
328
+ return ;
329
+ }
268
330
}
269
331
_ => { }
270
332
}
@@ -298,6 +360,10 @@ impl<'db> UnionBuilder<'db> {
298
360
continue ;
299
361
}
300
362
ReduceResult :: Type ( ty) => ty,
363
+ ReduceResult :: CollapseToObject => {
364
+ self . collapse_to_object ( ) ;
365
+ return ;
366
+ }
301
367
} ;
302
368
if Some ( element_type) == bool_pair {
303
369
to_add = KnownClass :: Bool . to_instance ( self . db ) ;
@@ -317,12 +383,14 @@ impl<'db> UnionBuilder<'db> {
317
383
} else if element_type. is_subtype_of ( self . db , ty) {
318
384
to_remove. push ( index) ;
319
385
} else if ty_negated. is_subtype_of ( self . db , element_type) {
320
- // We add `ty` to the union. We just checked that `~ty` is a subtype of an existing `element`.
321
- // This also means that `~ty | ty` is a subtype of `element | ty`, because both elements in the
322
- // first union are subtypes of the corresponding elements in the second union. But `~ty | ty` is
323
- // just `object`. Since `object` is a subtype of `element | ty`, we can only conclude that
324
- // `element | ty` must be `object` (object has no other supertypes). This means we can simplify
325
- // the whole union to just `object`, since all other potential elements would also be subtypes of
386
+ // We add `ty` to the union. We just checked that `~ty` is a subtype of an
387
+ // existing `element`. This also means that `~ty | ty` is a subtype of
388
+ // `element | ty`, because both elements in the first union are subtypes of
389
+ // the corresponding elements in the second union. But `~ty | ty` is just
390
+ // `object`. Since `object` is a subtype of `element | ty`, we can only
391
+ // conclude that `element | ty` must be `object` (object has no other
392
+ // supertypes). This means we can simplify the whole union to just
393
+ // `object`, since all other potential elements would also be subtypes of
326
394
// `object`.
327
395
self . collapse_to_object ( ) ;
328
396
return ;
0 commit comments