diff --git a/crates/red_knot_python_semantic/src/types.rs b/crates/red_knot_python_semantic/src/types.rs index f499790f5a3dd..7e412936dbf7c 100644 --- a/crates/red_knot_python_semantic/src/types.rs +++ b/crates/red_knot_python_semantic/src/types.rs @@ -574,8 +574,11 @@ impl<'db> Type<'db> { Self::BytesLiteral(BytesLiteralType::new(db, bytes)) } - pub fn tuple(db: &'db dyn Db, elements: &[Type<'db>]) -> Self { - Self::Tuple(TupleType::new(db, elements)) + pub fn tuple>>( + db: &'db dyn Db, + elements: impl IntoIterator, + ) -> Self { + TupleType::from_elements(db, elements) } #[must_use] @@ -3015,6 +3018,23 @@ pub struct TupleType<'db> { } impl<'db> TupleType<'db> { + pub fn from_elements>>( + db: &'db dyn Db, + types: impl IntoIterator, + ) -> Type<'db> { + let mut elements = vec![]; + + for ty in types { + let ty = ty.into(); + if ty.is_never() { + return Type::Never; + } + elements.push(ty); + } + + Type::Tuple(Self::new(db, elements.into_boxed_slice())) + } + pub fn get(&self, db: &'db dyn Db, index: usize) -> Option> { self.elements(db).get(index).copied() } @@ -3122,13 +3142,21 @@ pub(crate) mod tests { builder.build() } Ty::Tuple(tys) => { - let elements: Vec = tys.into_iter().map(|ty| ty.into_type(db)).collect(); - Type::tuple(db, &elements) + let elements = tys.into_iter().map(|ty| ty.into_type(db)); + Type::tuple(db, elements) } } } } + #[test_case(Ty::Tuple(vec![Ty::Never]))] + #[test_case(Ty::Tuple(vec![Ty::BuiltinInstance("str"), Ty::Never, Ty::BuiltinInstance("int")]))] + #[test_case(Ty::Tuple(vec![Ty::Tuple(vec![Ty::Never])]))] + fn tuple_containing_never_simplifies_to_never(ty: Ty) { + let db = setup_db(); + assert_eq!(ty.into_type(&db), Type::Never); + } + #[test_case(Ty::BuiltinInstance("str"), Ty::BuiltinInstance("object"))] #[test_case(Ty::BuiltinInstance("int"), Ty::BuiltinInstance("object"))] #[test_case(Ty::Unknown, Ty::IntLiteral(1))] diff --git a/crates/red_knot_python_semantic/src/types/infer.rs b/crates/red_knot_python_semantic/src/types/infer.rs index a27603d0415bf..9471a6fb1172d 100644 --- a/crates/red_knot_python_semantic/src/types/infer.rs +++ b/crates/red_knot_python_semantic/src/types/infer.rs @@ -4537,7 +4537,7 @@ impl<'db> TypeInferenceBuilder<'db> { if element_could_alter_type_of_whole_tuple(single_element, single_element_ty) { todo_type!() } else { - Type::tuple(self.db, &[single_element_ty]) + Type::tuple(self.db, [single_element_ty]) } } } diff --git a/crates/red_knot_python_semantic/src/types/unpacker.rs b/crates/red_knot_python_semantic/src/types/unpacker.rs index 2e8c39a9b23c0..dc96fac252af5 100644 --- a/crates/red_knot_python_semantic/src/types/unpacker.rs +++ b/crates/red_knot_python_semantic/src/types/unpacker.rs @@ -95,7 +95,7 @@ impl<'db> Unpacker<'db> { // there would be a cost and it's not clear that it's worth it. let value_ty = Type::tuple( self.db, - &vec![Type::LiteralString; string_literal_ty.len(self.db)], + std::iter::repeat(Type::LiteralString).take(string_literal_ty.len(self.db)), ); self.unpack(target, value_ty, scope); }