Skip to content

Commit fe308d6

Browse files
committed
Add `UnionBuilder::extend
1 parent 91e214c commit fe308d6

File tree

5 files changed

+58
-43
lines changed

5 files changed

+58
-43
lines changed

crates/red_knot_python_semantic/src/symbol.rs

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ impl<'db> LookupError<'db> {
157157
fallback: Symbol<'db>,
158158
) -> LookupResult<'db> {
159159
let fallback = fallback.into_lookup_result();
160-
match (&self, &fallback) {
160+
match (self, fallback) {
161161
(LookupError::Unbound, _) => fallback,
162162
(LookupError::PossiblyUnbound { .. }, Err(LookupError::Unbound)) => Err(self),
163163
(LookupError::PossiblyUnbound(ty), Ok(ty2)) => {
@@ -611,15 +611,17 @@ fn symbol_from_declarations_impl<'db>(
611611
let ty_first = first.inner_type();
612612
let mut qualifiers = first.qualifiers();
613613

614-
let mut builder = UnionBuilder::new(db).add(ty_first);
615-
for other in std::iter::once(second).chain(types) {
616-
let other_ty = other.inner_type();
617-
if !ty_first.is_equivalent_to(db, other_ty) {
618-
conflicting.push(other_ty);
619-
}
620-
builder = builder.add(other_ty);
621-
qualifiers = qualifiers.union(other.qualifiers());
622-
}
614+
let mut builder = UnionBuilder::new(db).add(ty_first).extend(
615+
std::iter::once(second).chain(types).map(|other| {
616+
let other_ty = other.inner_type();
617+
if !ty_first.is_equivalent_to(db, other_ty) {
618+
conflicting.push(other_ty);
619+
}
620+
621+
qualifiers = qualifiers.union(other.qualifiers());
622+
other_ty
623+
}),
624+
);
623625
TypeAndQualifiers::new(builder.build(), qualifiers)
624626
} else {
625627
first

crates/red_knot_python_semantic/src/types.rs

Lines changed: 15 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1796,7 +1796,7 @@ impl<'db> Type<'db> {
17961796
fn iterate(self, db: &'db dyn Db) -> IterationOutcome<'db> {
17971797
if let Type::Tuple(tuple_type) = self {
17981798
return IterationOutcome::Iterable {
1799-
element_ty: UnionType::from_elements(db, tuple_type.elements(db)),
1799+
element_ty: UnionType::from_elements(db, tuple_type.elements(db).iter().copied()),
18001800
};
18011801
}
18021802

@@ -3700,12 +3700,13 @@ impl<'db> Class<'db> {
37003700
(Some(builder), Some(ty)) => Some(builder.add(ty)),
37013701
}
37023702
})
3703-
.map(|mut builder| {
3704-
for binding in bindings {
3705-
builder = builder.add(binding.return_type());
3706-
}
3707-
3708-
builder.build()
3703+
.map(|builder| {
3704+
builder
3705+
.extend(
3706+
IntoIterator::into_iter(bindings)
3707+
.map(|binding| binding.return_type()),
3708+
)
3709+
.build()
37093710
});
37103711

37113712
if partly_not_callable {
@@ -4172,27 +4173,24 @@ impl<'db> UnionType<'db> {
41724173

41734174
/// Create a union from a list of elements
41744175
/// (which may be eagerly simplified into a different variant of [`Type`] altogether).
4175-
pub fn from_elements<I, T>(db: &'db dyn Db, elements: I) -> Type<'db>
4176+
pub fn from_elements<I>(db: &'db dyn Db, elements: I) -> Type<'db>
41764177
where
4177-
I: IntoIterator<Item = T>,
4178-
T: Into<Type<'db>>,
4178+
I: IntoIterator<Item = Type<'db>>,
41794179
{
41804180
let mut elements = elements.into_iter();
41814181
let Some(first) = elements.next() else {
41824182
return Type::Never;
41834183
};
41844184

41854185
let Some(second) = elements.next() else {
4186-
return first.into();
4186+
return first;
41874187
};
41884188

4189-
let mut builder = UnionBuilder::new(db).add(first.into()).add(second.into());
4190-
4191-
for element in elements {
4192-
builder = builder.add(element.into());
4193-
}
4189+
let (lower, _) = elements.size_hint();
4190+
let mut builder = UnionBuilder::new(db);
4191+
builder.reserve(lower + 2);
41944192

4195-
builder.build()
4193+
builder.add(first).add(second).extend(elements).build()
41964194
}
41974195

41984196
/// Apply a transformation function to all elements of the union,

crates/red_knot_python_semantic/src/types/builder.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,13 +43,31 @@ impl<'db> UnionBuilder<'db> {
4343
}
4444
}
4545

46+
pub(crate) fn reserve(&mut self, additional: usize) {
47+
self.elements.reserve(additional);
48+
}
49+
4650
/// Collapse the union to a single type: `object`.
4751
fn collapse_to_object(mut self) -> Self {
4852
self.elements.clear();
4953
self.elements.push(Type::object(self.db));
5054
self
5155
}
5256

57+
pub(crate) fn extend(mut self, elements: impl IntoIterator<Item = Type<'db>>) -> Self {
58+
let elements = elements.into_iter();
59+
let (lower, _) = elements.size_hint();
60+
61+
// Assume that most types will be unique
62+
self.reserve(lower);
63+
64+
for element in elements {
65+
self = self.add(element);
66+
}
67+
68+
self
69+
}
70+
5371
/// Adds a type to this union.
5472
pub(crate) fn add(mut self, ty: Type<'db>) -> Self {
5573
match ty {

crates/red_knot_python_semantic/src/types/infer.rs

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1707,18 +1707,17 @@ impl<'db> TypeInferenceBuilder<'db> {
17071707
let symbol_ty = if let Type::Tuple(tuple) = node_ty {
17081708
let type_base_exception = KnownClass::BaseException.to_subclass_of(self.db());
17091709
let mut builder = UnionBuilder::new(self.db());
1710-
for element in tuple.elements(self.db()).iter().copied() {
1711-
builder = builder.add(
1712-
if element.is_assignable_to(self.db(), type_base_exception) {
1713-
element.to_instance(self.db())
1714-
} else {
1715-
if let Some(node) = node {
1716-
report_invalid_exception_caught(&self.context, node, element);
1717-
}
1718-
Type::unknown()
1719-
},
1720-
);
1721-
}
1710+
builder = builder.extend(tuple.elements(self.db()).iter().map(|element| {
1711+
if element.is_assignable_to(self.db(), type_base_exception) {
1712+
element.to_instance(self.db())
1713+
} else {
1714+
if let Some(node) = node {
1715+
report_invalid_exception_caught(&self.context, node, *element);
1716+
}
1717+
Type::unknown()
1718+
}
1719+
}));
1720+
17221721
builder.build()
17231722
} else if node_ty.is_subtype_of(self.db(), KnownClass::Tuple.to_instance(self.db())) {
17241723
todo_type!("Homogeneous tuple in exception handler")
@@ -3363,9 +3362,7 @@ impl<'db> TypeInferenceBuilder<'db> {
33633362
bindings: _,
33643363
errors,
33653364
} => {
3366-
// TODO: Remove the `Vec::from` call once we use the Rust 2024 edition
3367-
// which adds `Box<[T]>::into_iter`
3368-
if let Some(first) = Vec::from(errors).into_iter().next() {
3365+
if let Some(first) = IntoIterator::into_iter(errors).next() {
33693366
report_call_error(context, first, call_expression);
33703367
} else {
33713368
debug_assert!(

crates/red_knot_python_semantic/src/types/unpacker.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ impl<'db> Unpacker<'db> {
168168
// SAFETY: `target_types` is initialized with the same length as `elts`.
169169
let element_ty = match target_types[index].as_slice() {
170170
[] => Type::unknown(),
171-
types => UnionType::from_elements(self.db(), types),
171+
types => UnionType::from_elements(self.db(), types.iter().copied()),
172172
};
173173
self.unpack_inner(element, value_expr, element_ty);
174174
}

0 commit comments

Comments
 (0)