[red-knot] More Type constructors (#14227)

This commit is contained in:
Alex Waygood 2024-11-09 16:57:11 +00:00 committed by GitHub
parent c9b84e2a85
commit e598240f04
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 84 additions and 115 deletions

View file

@ -478,6 +478,18 @@ impl<'db> Type<'db> {
Self::SubclassOf(SubclassOfType { class }) Self::SubclassOf(SubclassOfType { class })
} }
pub fn string_literal(db: &'db dyn Db, string: &str) -> Self {
Self::StringLiteral(StringLiteralType::new(db, string))
}
pub fn bytes_literal(db: &'db dyn Db, bytes: &[u8]) -> Self {
Self::BytesLiteral(BytesLiteralType::new(db, bytes))
}
pub fn tuple(db: &'db dyn Db, elements: &[Type<'db>]) -> Self {
Self::Tuple(TupleType::new(db, elements))
}
#[must_use] #[must_use]
pub fn negate(&self, db: &'db dyn Db) -> Type<'db> { pub fn negate(&self, db: &'db dyn Db) -> Type<'db> {
IntersectionBuilder::new(db).add_negative(*self).build() IntersectionBuilder::new(db).add_negative(*self).build()
@ -1458,7 +1470,7 @@ impl<'db> Type<'db> {
Type::IntLiteral(_) | Type::BooleanLiteral(_) => self.repr(db), Type::IntLiteral(_) | Type::BooleanLiteral(_) => self.repr(db),
Type::StringLiteral(_) | Type::LiteralString => *self, Type::StringLiteral(_) | Type::LiteralString => *self,
Type::KnownInstance(known_instance) => { Type::KnownInstance(known_instance) => {
Type::StringLiteral(StringLiteralType::new(db, known_instance.repr(db))) Type::string_literal(db, known_instance.repr(db))
} }
// TODO: handle more complex types // TODO: handle more complex types
_ => KnownClass::Str.to_instance(db), _ => KnownClass::Str.to_instance(db),
@ -1470,17 +1482,15 @@ impl<'db> Type<'db> {
#[must_use] #[must_use]
pub fn repr(&self, db: &'db dyn Db) -> Type<'db> { pub fn repr(&self, db: &'db dyn Db) -> Type<'db> {
match self { match self {
Type::IntLiteral(number) => Type::StringLiteral(StringLiteralType::new(db, { Type::IntLiteral(number) => Type::string_literal(db, &number.to_string()),
number.to_string().into_boxed_str() Type::BooleanLiteral(true) => Type::string_literal(db, "True"),
})), Type::BooleanLiteral(false) => Type::string_literal(db, "False"),
Type::BooleanLiteral(true) => Type::StringLiteral(StringLiteralType::new(db, "True")), Type::StringLiteral(literal) => {
Type::BooleanLiteral(false) => Type::StringLiteral(StringLiteralType::new(db, "False")), Type::string_literal(db, &format!("'{}'", literal.value(db).escape_default()))
Type::StringLiteral(literal) => Type::StringLiteral(StringLiteralType::new(db, { }
format!("'{}'", literal.value(db).escape_default()).into_boxed_str()
})),
Type::LiteralString => Type::LiteralString, Type::LiteralString => Type::LiteralString,
Type::KnownInstance(known_instance) => { Type::KnownInstance(known_instance) => {
Type::StringLiteral(StringLiteralType::new(db, known_instance.repr(db))) Type::string_literal(db, known_instance.repr(db))
} }
// TODO: handle more complex types // TODO: handle more complex types
_ => KnownClass::Str.to_instance(db), _ => KnownClass::Str.to_instance(db),
@ -1738,42 +1748,28 @@ impl<'db> KnownInstanceType<'db> {
} }
fn member(self, db: &'db dyn Db, name: &str) -> Symbol<'db> { fn member(self, db: &'db dyn Db, name: &str) -> Symbol<'db> {
match (self, name) { let ty = match (self, name) {
(Self::TypeVar(typevar), "__name__") => Symbol::Type( (Self::TypeVar(typevar), "__name__") => Type::string_literal(db, typevar.name(db)),
Type::StringLiteral(StringLiteralType::new(db, typevar.name(db).as_str())), (Self::TypeVar(typevar), "__bound__") => typevar
Boundness::Bound, .upper_bound(db)
), .map(|ty| ty.to_meta_type(db))
(Self::TypeVar(typevar), "__bound__") => Symbol::Type( .unwrap_or_else(|| KnownClass::NoneType.to_instance(db)),
typevar (Self::TypeVar(typevar), "__constraints__") => {
.upper_bound(db) let tuple_elements: Vec<Type<'db>> = typevar
.constraints(db)
.unwrap_or_default()
.iter()
.map(|ty| ty.to_meta_type(db)) .map(|ty| ty.to_meta_type(db))
.unwrap_or(KnownClass::NoneType.to_instance(db)), .collect();
Boundness::Bound, Type::tuple(db, &tuple_elements)
), }
(Self::TypeVar(typevar), "__constraints__") => Symbol::Type( (Self::TypeVar(typevar), "__default__") => typevar
Type::Tuple(TupleType::new( .default_ty(db)
db, .map(|ty| ty.to_meta_type(db))
typevar .unwrap_or_else(|| KnownClass::NoDefaultType.to_instance(db)),
.constraints(db) _ => return self.instance_fallback(db).member(db, name),
.map(|constraints| { };
constraints ty.into()
.iter()
.map(|ty| ty.to_meta_type(db))
.collect::<Box<_>>()
})
.unwrap_or_else(|| std::iter::empty().collect::<Box<_>>()),
)),
Boundness::Bound,
),
(Self::TypeVar(typevar), "__default__") => Symbol::Type(
typevar
.default_ty(db)
.map(|ty| ty.to_meta_type(db))
.unwrap_or_else(|| KnownClass::NoDefaultType.to_instance(db)),
Boundness::Bound,
),
_ => self.instance_fallback(db).member(db, name),
}
} }
} }
@ -2544,11 +2540,8 @@ impl<'db> Class<'db> {
/// The member resolves to a member on the class itself or any of its proper superclasses. /// The member resolves to a member on the class itself or any of its proper superclasses.
pub(crate) fn class_member(self, db: &'db dyn Db, name: &str) -> Symbol<'db> { pub(crate) fn class_member(self, db: &'db dyn Db, name: &str) -> Symbol<'db> {
if name == "__mro__" { if name == "__mro__" {
let tuple_elements: Box<_> = self.iter_mro(db).map(Type::from).collect(); let tuple_elements: Vec<Type<'db>> = self.iter_mro(db).map(Type::from).collect();
return Symbol::Type( return Type::tuple(db, &tuple_elements).into();
Type::Tuple(TupleType::new(db, tuple_elements)),
Boundness::Bound,
);
} }
if name == "__class__" { if name == "__class__" {
@ -2880,10 +2873,10 @@ mod tests {
Ty::Any => Type::Any, Ty::Any => Type::Any,
Ty::Todo => Type::Todo, Ty::Todo => Type::Todo,
Ty::IntLiteral(n) => Type::IntLiteral(n), Ty::IntLiteral(n) => Type::IntLiteral(n),
Ty::StringLiteral(s) => Type::StringLiteral(StringLiteralType::new(db, s)), Ty::StringLiteral(s) => Type::string_literal(db, s),
Ty::BooleanLiteral(b) => Type::BooleanLiteral(b), Ty::BooleanLiteral(b) => Type::BooleanLiteral(b),
Ty::LiteralString => Type::LiteralString, Ty::LiteralString => Type::LiteralString,
Ty::BytesLiteral(s) => Type::BytesLiteral(BytesLiteralType::new(db, s.as_bytes())), Ty::BytesLiteral(s) => Type::bytes_literal(db, s.as_bytes()),
Ty::BuiltinInstance(s) => builtins_symbol(db, s).expect_type().to_instance(db), Ty::BuiltinInstance(s) => builtins_symbol(db, s).expect_type().to_instance(db),
Ty::TypingInstance(s) => typing_symbol(db, s).expect_type().to_instance(db), Ty::TypingInstance(s) => typing_symbol(db, s).expect_type().to_instance(db),
Ty::TypingLiteral => Type::KnownInstance(KnownInstanceType::Literal), Ty::TypingLiteral => Type::KnownInstance(KnownInstanceType::Literal),
@ -2903,8 +2896,8 @@ mod tests {
builder.build() builder.build()
} }
Ty::Tuple(tys) => { Ty::Tuple(tys) => {
let elements: Box<_> = tys.into_iter().map(|ty| ty.into_type(db)).collect(); let elements: Vec<Type> = tys.into_iter().map(|ty| ty.into_type(db)).collect();
Type::Tuple(TupleType::new(db, elements)) Type::tuple(db, &elements)
} }
} }
} }

View file

@ -383,7 +383,7 @@ mod tests {
use crate::program::{Program, SearchPathSettings}; use crate::program::{Program, SearchPathSettings};
use crate::python_version::PythonVersion; use crate::python_version::PythonVersion;
use crate::stdlib::typing_symbol; use crate::stdlib::typing_symbol;
use crate::types::{global_symbol, KnownClass, StringLiteralType, UnionBuilder}; use crate::types::{global_symbol, KnownClass, UnionBuilder};
use crate::ProgramSettings; use crate::ProgramSettings;
use ruff_db::files::system_path_to_file; use ruff_db::files::system_path_to_file;
use ruff_db::system::{DbWithTestSystem, SystemPathBuf}; use ruff_db::system::{DbWithTestSystem, SystemPathBuf};
@ -775,7 +775,7 @@ mod tests {
.build(); .build();
assert_eq!(ty, s); assert_eq!(ty, s);
let literal = Type::StringLiteral(StringLiteralType::new(&db, "a")); let literal = Type::string_literal(&db, "a");
let expected = IntersectionBuilder::new(&db) let expected = IntersectionBuilder::new(&db)
.add_positive(s) .add_positive(s)
.add_negative(literal) .add_negative(literal)
@ -878,7 +878,7 @@ mod tests {
let ty = IntersectionBuilder::new(&db) let ty = IntersectionBuilder::new(&db)
.add_positive(s) .add_positive(s)
.add_negative(Type::StringLiteral(StringLiteralType::new(&db, "a"))) .add_negative(Type::string_literal(&db, "a"))
.add_negative(t) .add_negative(t)
.build(); .build();
assert_eq!(ty, Type::Never); assert_eq!(ty, Type::Never);
@ -912,7 +912,7 @@ mod tests {
let db = setup_db(); let db = setup_db();
let t_p = KnownClass::Int.to_instance(&db); let t_p = KnownClass::Int.to_instance(&db);
let t_n = Type::StringLiteral(StringLiteralType::new(&db, "t_n")); let t_n = Type::string_literal(&db, "t_n");
let ty = IntersectionBuilder::new(&db) let ty = IntersectionBuilder::new(&db)
.add_positive(t_p) .add_positive(t_p)

View file

@ -337,9 +337,7 @@ mod tests {
use ruff_db::system::{DbWithTestSystem, SystemPathBuf}; use ruff_db::system::{DbWithTestSystem, SystemPathBuf};
use crate::db::tests::TestDb; use crate::db::tests::TestDb;
use crate::types::{ use crate::types::{global_symbol, SliceLiteralType, Type, UnionType};
global_symbol, BytesLiteralType, SliceLiteralType, StringLiteralType, Type, UnionType,
};
use crate::{Program, ProgramSettings, PythonVersion, SearchPathSettings}; use crate::{Program, ProgramSettings, PythonVersion, SearchPathSettings};
fn setup_db() -> TestDb { fn setup_db() -> TestDb {
@ -385,12 +383,12 @@ mod tests {
Type::Unknown, Type::Unknown,
Type::IntLiteral(-1), Type::IntLiteral(-1),
global_symbol(&db, mod_file, "A").expect_type(), global_symbol(&db, mod_file, "A").expect_type(),
Type::StringLiteral(StringLiteralType::new(&db, "A")), Type::string_literal(&db, "A"),
Type::BytesLiteral(BytesLiteralType::new(&db, [0u8].as_slice())), Type::bytes_literal(&db, &[0u8]),
Type::BytesLiteral(BytesLiteralType::new(&db, [7u8].as_slice())), Type::bytes_literal(&db, &[7u8]),
Type::IntLiteral(0), Type::IntLiteral(0),
Type::IntLiteral(1), Type::IntLiteral(1),
Type::StringLiteral(StringLiteralType::new(&db, "B")), Type::string_literal(&db, "B"),
global_symbol(&db, mod_file, "foo").expect_type(), global_symbol(&db, mod_file, "foo").expect_type(),
global_symbol(&db, mod_file, "bar").expect_type(), global_symbol(&db, mod_file, "bar").expect_type(),
global_symbol(&db, mod_file, "B").expect_type(), global_symbol(&db, mod_file, "B").expect_type(),

View file

@ -56,11 +56,10 @@ use crate::types::mro::MroErrorKind;
use crate::types::unpacker::{UnpackResult, Unpacker}; use crate::types::unpacker::{UnpackResult, Unpacker};
use crate::types::{ use crate::types::{
bindings_ty, builtins_symbol, declarations_ty, global_symbol, symbol, typing_extensions_symbol, bindings_ty, builtins_symbol, declarations_ty, global_symbol, symbol, typing_extensions_symbol,
Boundness, BytesLiteralType, Class, ClassLiteralType, FunctionType, InstanceType, Boundness, Class, ClassLiteralType, FunctionType, InstanceType, IntersectionBuilder,
IntersectionBuilder, IntersectionType, IterationOutcome, KnownClass, KnownFunction, IntersectionType, IterationOutcome, KnownClass, KnownFunction, KnownInstanceType,
KnownInstanceType, MetaclassCandidate, MetaclassErrorKind, SliceLiteralType, StringLiteralType, MetaclassCandidate, MetaclassErrorKind, SliceLiteralType, Symbol, Truthiness, TupleType, Type,
Symbol, Truthiness, TupleType, Type, TypeArrayDisplay, TypeVarBoundOrConstraints, TypeArrayDisplay, TypeVarBoundOrConstraints, TypeVarInstance, UnionBuilder, UnionType,
TypeVarInstance, UnionBuilder, UnionType,
}; };
use crate::unpack::Unpack; use crate::unpack::Unpack;
use crate::util::subscript::{PyIndex, PySlice}; use crate::util::subscript::{PyIndex, PySlice};
@ -2202,7 +2201,7 @@ impl<'db> TypeInferenceBuilder<'db> {
fn infer_string_literal_expression(&mut self, literal: &ast::ExprStringLiteral) -> Type<'db> { fn infer_string_literal_expression(&mut self, literal: &ast::ExprStringLiteral) -> Type<'db> {
if literal.value.len() <= Self::MAX_STRING_LITERAL_SIZE { if literal.value.len() <= Self::MAX_STRING_LITERAL_SIZE {
Type::StringLiteral(StringLiteralType::new(self.db, literal.value.to_str())) Type::string_literal(self.db, literal.value.to_str())
} else { } else {
Type::LiteralString Type::LiteralString
} }
@ -2210,10 +2209,8 @@ impl<'db> TypeInferenceBuilder<'db> {
fn infer_bytes_literal_expression(&mut self, literal: &ast::ExprBytesLiteral) -> Type<'db> { fn infer_bytes_literal_expression(&mut self, literal: &ast::ExprBytesLiteral) -> Type<'db> {
// TODO: ignoring r/R prefixes for now, should normalize bytes values // TODO: ignoring r/R prefixes for now, should normalize bytes values
Type::BytesLiteral(BytesLiteralType::new( let bytes: Vec<u8> = literal.value.bytes().collect();
self.db, Type::bytes_literal(self.db, &bytes)
literal.value.bytes().collect::<Box<[u8]>>(),
))
} }
fn infer_fstring_expression(&mut self, fstring: &ast::ExprFString) -> Type<'db> { fn infer_fstring_expression(&mut self, fstring: &ast::ExprFString) -> Type<'db> {
@ -2280,12 +2277,10 @@ impl<'db> TypeInferenceBuilder<'db> {
parenthesized: _, parenthesized: _,
} = tuple; } = tuple;
let element_types = elts let element_types: Vec<Type<'db>> =
.iter() elts.iter().map(|elt| self.infer_expression(elt)).collect();
.map(|elt| self.infer_expression(elt))
.collect::<Vec<_>>();
Type::Tuple(TupleType::new(self.db, element_types.into_boxed_slice())) Type::tuple(self.db, &element_types)
} }
fn infer_list_expression(&mut self, list: &ast::ExprList) -> Type<'db> { fn infer_list_expression(&mut self, list: &ast::ExprList) -> Type<'db> {
@ -2987,21 +2982,15 @@ impl<'db> TypeInferenceBuilder<'db> {
} }
(Type::BytesLiteral(lhs), Type::BytesLiteral(rhs), ast::Operator::Add) => { (Type::BytesLiteral(lhs), Type::BytesLiteral(rhs), ast::Operator::Add) => {
Some(Type::BytesLiteral(BytesLiteralType::new( let bytes = [&**lhs.value(self.db), &**rhs.value(self.db)].concat();
self.db, Some(Type::bytes_literal(self.db, &bytes))
[lhs.value(self.db).as_ref(), rhs.value(self.db).as_ref()]
.concat()
.into_boxed_slice(),
)))
} }
(Type::StringLiteral(lhs), Type::StringLiteral(rhs), ast::Operator::Add) => { (Type::StringLiteral(lhs), Type::StringLiteral(rhs), ast::Operator::Add) => {
let lhs_value = lhs.value(self.db).to_string(); let lhs_value = lhs.value(self.db).to_string();
let rhs_value = rhs.value(self.db).as_ref(); let rhs_value = rhs.value(self.db).as_ref();
let ty = if lhs_value.len() + rhs_value.len() <= Self::MAX_STRING_LITERAL_SIZE { let ty = if lhs_value.len() + rhs_value.len() <= Self::MAX_STRING_LITERAL_SIZE {
Type::StringLiteral(StringLiteralType::new(self.db, { Type::string_literal(self.db, &(lhs_value + rhs_value))
(lhs_value + rhs_value).into_boxed_str()
}))
} else { } else {
Type::LiteralString Type::LiteralString
}; };
@ -3017,16 +3006,13 @@ impl<'db> TypeInferenceBuilder<'db> {
(Type::StringLiteral(s), Type::IntLiteral(n), ast::Operator::Mult) (Type::StringLiteral(s), Type::IntLiteral(n), ast::Operator::Mult)
| (Type::IntLiteral(n), Type::StringLiteral(s), ast::Operator::Mult) => { | (Type::IntLiteral(n), Type::StringLiteral(s), ast::Operator::Mult) => {
let ty = if n < 1 { let ty = if n < 1 {
Type::StringLiteral(StringLiteralType::new(self.db, "")) Type::string_literal(self.db, "")
} else if let Ok(n) = usize::try_from(n) { } else if let Ok(n) = usize::try_from(n) {
if n.checked_mul(s.value(self.db).len()) if n.checked_mul(s.value(self.db).len())
.is_some_and(|new_length| new_length <= Self::MAX_STRING_LITERAL_SIZE) .is_some_and(|new_length| new_length <= Self::MAX_STRING_LITERAL_SIZE)
{ {
let new_literal = s.value(self.db).repeat(n); let new_literal = s.value(self.db).repeat(n);
Type::StringLiteral(StringLiteralType::new( Type::string_literal(self.db, &new_literal)
self.db,
new_literal.into_boxed_str(),
))
} else { } else {
Type::LiteralString Type::LiteralString
} }
@ -3039,7 +3025,7 @@ impl<'db> TypeInferenceBuilder<'db> {
(Type::LiteralString, Type::IntLiteral(n), ast::Operator::Mult) (Type::LiteralString, Type::IntLiteral(n), ast::Operator::Mult)
| (Type::IntLiteral(n), Type::LiteralString, ast::Operator::Mult) => { | (Type::IntLiteral(n), Type::LiteralString, ast::Operator::Mult) => {
let ty = if n < 1 { let ty = if n < 1 {
Type::StringLiteral(StringLiteralType::new(self.db, "")) Type::string_literal(self.db, "")
} else { } else {
Type::LiteralString Type::LiteralString
}; };
@ -3752,7 +3738,7 @@ impl<'db> TypeInferenceBuilder<'db> {
if let Ok(new_elements) = elements.py_slice(start, stop, step) { if let Ok(new_elements) = elements.py_slice(start, stop, step) {
let new_elements: Vec<_> = new_elements.copied().collect(); let new_elements: Vec<_> = new_elements.copied().collect();
Type::Tuple(TupleType::new(self.db, new_elements.into_boxed_slice())) Type::tuple(self.db, &new_elements)
} else { } else {
self.diagnostics.add_slice_step_size_zero(value_node.into()); self.diagnostics.add_slice_step_size_zero(value_node.into());
Type::Unknown Type::Unknown
@ -3766,12 +3752,7 @@ impl<'db> TypeInferenceBuilder<'db> {
literal_value literal_value
.chars() .chars()
.py_index(i32::try_from(int).expect("checked in branch arm")) .py_index(i32::try_from(int).expect("checked in branch arm"))
.map(|ch| { .map(|ch| Type::string_literal(self.db, &ch.to_string()))
Type::StringLiteral(StringLiteralType::new(
self.db,
ch.to_string().into_boxed_str(),
))
})
.unwrap_or_else(|_| { .unwrap_or_else(|_| {
self.diagnostics.add_index_out_of_bounds( self.diagnostics.add_index_out_of_bounds(
"string", "string",
@ -3791,7 +3772,7 @@ impl<'db> TypeInferenceBuilder<'db> {
let chars: Vec<_> = literal_value.chars().collect(); let chars: Vec<_> = literal_value.chars().collect();
let result = if let Ok(new_chars) = chars.py_slice(start, stop, step) { let result = if let Ok(new_chars) = chars.py_slice(start, stop, step) {
let literal: String = new_chars.collect(); let literal: String = new_chars.collect();
Type::StringLiteral(StringLiteralType::new(self.db, literal.into_boxed_str())) Type::string_literal(self.db, &literal)
} else { } else {
self.diagnostics.add_slice_step_size_zero(value_node.into()); self.diagnostics.add_slice_step_size_zero(value_node.into());
Type::Unknown Type::Unknown
@ -3806,9 +3787,7 @@ impl<'db> TypeInferenceBuilder<'db> {
literal_value literal_value
.iter() .iter()
.py_index(i32::try_from(int).expect("checked in branch arm")) .py_index(i32::try_from(int).expect("checked in branch arm"))
.map(|byte| { .map(|byte| Type::bytes_literal(self.db, &[*byte]))
Type::BytesLiteral(BytesLiteralType::new(self.db, [*byte].as_slice()))
})
.unwrap_or_else(|_| { .unwrap_or_else(|_| {
self.diagnostics.add_index_out_of_bounds( self.diagnostics.add_index_out_of_bounds(
"bytes literal", "bytes literal",
@ -3827,7 +3806,7 @@ impl<'db> TypeInferenceBuilder<'db> {
if let Ok(new_bytes) = literal_value.py_slice(start, stop, step) { if let Ok(new_bytes) = literal_value.py_slice(start, stop, step) {
let new_bytes: Vec<u8> = new_bytes.copied().collect(); let new_bytes: Vec<u8> = new_bytes.copied().collect();
Type::BytesLiteral(BytesLiteralType::new(self.db, new_bytes.into_boxed_slice())) Type::bytes_literal(self.db, &new_bytes)
} else { } else {
self.diagnostics.add_slice_step_size_zero(value_node.into()); self.diagnostics.add_slice_step_size_zero(value_node.into());
Type::Unknown Type::Unknown
@ -4262,7 +4241,7 @@ impl<'db> TypeInferenceBuilder<'db> {
if return_todo { if return_todo {
Type::Todo Type::Todo
} else { } else {
Type::Tuple(TupleType::new(self.db, element_types.into_boxed_slice())) Type::tuple(self.db, &element_types)
} }
} }
single_element => { single_element => {
@ -4270,7 +4249,7 @@ impl<'db> TypeInferenceBuilder<'db> {
if element_could_alter_type_of_whole_tuple(single_element, single_element_ty) { if element_could_alter_type_of_whole_tuple(single_element, single_element_ty) {
Type::Todo Type::Todo
} else { } else {
Type::Tuple(TupleType::new(self.db, Box::from([single_element_ty]))) Type::tuple(self.db, &[single_element_ty])
} }
} }
} }
@ -4541,7 +4520,7 @@ impl StringPartsCollector {
if self.expression { if self.expression {
KnownClass::Str.to_instance(db) KnownClass::Str.to_instance(db)
} else if let Some(concatenated) = self.concatenated { } else if let Some(concatenated) = self.concatenated {
Type::StringLiteral(StringLiteralType::new(db, concatenated.into_boxed_str())) Type::string_literal(db, &concatenated)
} else { } else {
Type::LiteralString Type::LiteralString
} }

View file

@ -6,7 +6,7 @@ use rustc_hash::FxHashMap;
use crate::semantic_index::ast_ids::{HasScopedAstId, ScopedExpressionId}; use crate::semantic_index::ast_ids::{HasScopedAstId, ScopedExpressionId};
use crate::semantic_index::symbol::ScopeId; use crate::semantic_index::symbol::ScopeId;
use crate::types::{TupleType, Type, TypeCheckDiagnostics, TypeCheckDiagnosticsBuilder}; use crate::types::{Type, TypeCheckDiagnostics, TypeCheckDiagnosticsBuilder};
use crate::Db; use crate::Db;
/// Unpacks the value expression type to their respective targets. /// Unpacks the value expression type to their respective targets.
@ -93,11 +93,10 @@ impl<'db> Unpacker<'db> {
// further and deconstruct to an array of `StringLiteral` with each // further and deconstruct to an array of `StringLiteral` with each
// individual character, instead of just an array of `LiteralString`, but // individual character, instead of just an array of `LiteralString`, but
// there would be a cost and it's not clear that it's worth it. // there would be a cost and it's not clear that it's worth it.
let value_ty = Type::Tuple(TupleType::new( let value_ty = Type::tuple(
self.db, self.db,
vec![Type::LiteralString; string_literal_ty.len(self.db)] &vec![Type::LiteralString; string_literal_ty.len(self.db)],
.into_boxed_slice(), );
));
self.unpack(target, value_ty, scope); self.unpack(target, value_ty, scope);
} }
_ => { _ => {