mirror of
https://github.com/astral-sh/ruff
synced 2026-01-21 13:30:49 -05:00
[ty] Reduce monomorphization (#22195)
This commit is contained in:
@@ -10889,25 +10889,37 @@ impl<'db> UnionTypeInstance<'db> {
|
||||
scope_id: ScopeId<'db>,
|
||||
typevar_binding_context: Option<Definition<'db>>,
|
||||
) -> Type<'db> {
|
||||
let value_expr_types = value_expr_types.into_iter().collect::<Box<_>>();
|
||||
|
||||
let mut builder = UnionBuilder::new(db);
|
||||
for ty in &value_expr_types {
|
||||
match ty.in_type_expression(db, scope_id, typevar_binding_context) {
|
||||
Ok(ty) => builder.add_in_place(ty),
|
||||
Err(error) => {
|
||||
return Type::KnownInstance(KnownInstanceType::UnionType(
|
||||
UnionTypeInstance::new(db, Some(value_expr_types), Err(error)),
|
||||
));
|
||||
fn from_value_expression_types_impl<'db>(
|
||||
db: &'db dyn Db,
|
||||
value_expr_types: Box<[Type<'db>]>,
|
||||
scope_id: ScopeId<'db>,
|
||||
typevar_binding_context: Option<Definition<'db>>,
|
||||
) -> Type<'db> {
|
||||
let mut builder = UnionBuilder::new(db);
|
||||
for ty in &value_expr_types {
|
||||
match ty.in_type_expression(db, scope_id, typevar_binding_context) {
|
||||
Ok(ty) => builder.add_in_place(ty),
|
||||
Err(error) => {
|
||||
return Type::KnownInstance(KnownInstanceType::UnionType(
|
||||
UnionTypeInstance::new(db, Some(value_expr_types), Err(error)),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Type::KnownInstance(KnownInstanceType::UnionType(UnionTypeInstance::new(
|
||||
db,
|
||||
Some(value_expr_types),
|
||||
Ok(builder.build()),
|
||||
)))
|
||||
}
|
||||
|
||||
Type::KnownInstance(KnownInstanceType::UnionType(UnionTypeInstance::new(
|
||||
from_value_expression_types_impl(
|
||||
db,
|
||||
Some(value_expr_types),
|
||||
Ok(builder.build()),
|
||||
)))
|
||||
value_expr_types.into_iter().collect(),
|
||||
scope_id,
|
||||
typevar_binding_context,
|
||||
)
|
||||
}
|
||||
|
||||
/// Get the types of the elements of this union as they would appear in a value
|
||||
|
||||
@@ -5124,27 +5124,46 @@ impl KnownClass {
|
||||
db: &'db dyn Db,
|
||||
specialization: impl IntoIterator<Item = Type<'db>>,
|
||||
) -> Option<ClassType<'db>> {
|
||||
fn to_specialized_class_type_impl<'db>(
|
||||
db: &'db dyn Db,
|
||||
class: KnownClass,
|
||||
class_literal: ClassLiteral<'db>,
|
||||
specialization: Box<[Type<'db>]>,
|
||||
generic_context: GenericContext<'db>,
|
||||
) -> ClassType<'db> {
|
||||
if specialization.len() != generic_context.len(db) {
|
||||
// a cache of the `KnownClass`es that we have already seen mismatched-arity
|
||||
// specializations for (and therefore that we've already logged a warning for)
|
||||
static MESSAGES: LazyLock<Mutex<FxHashSet<KnownClass>>> =
|
||||
LazyLock::new(Mutex::default);
|
||||
if MESSAGES.lock().unwrap().insert(class) {
|
||||
tracing::info!(
|
||||
"Wrong number of types when specializing {}. \
|
||||
Falling back to default specialization for the symbol instead.",
|
||||
class.display(db)
|
||||
);
|
||||
}
|
||||
return class_literal.default_specialization(db);
|
||||
}
|
||||
|
||||
class_literal
|
||||
.apply_specialization(db, |_| generic_context.specialize(db, specialization))
|
||||
}
|
||||
|
||||
let Type::ClassLiteral(class_literal) = self.to_class_literal(db) else {
|
||||
return None;
|
||||
};
|
||||
|
||||
let generic_context = class_literal.generic_context(db)?;
|
||||
|
||||
let types = specialization.into_iter().collect::<Box<[_]>>();
|
||||
if types.len() != generic_context.len(db) {
|
||||
// a cache of the `KnownClass`es that we have already seen mismatched-arity
|
||||
// specializations for (and therefore that we've already logged a warning for)
|
||||
static MESSAGES: LazyLock<Mutex<FxHashSet<KnownClass>>> = LazyLock::new(Mutex::default);
|
||||
if MESSAGES.lock().unwrap().insert(self) {
|
||||
tracing::info!(
|
||||
"Wrong number of types when specializing {}. \
|
||||
Falling back to default specialization for the symbol instead.",
|
||||
self.display(db)
|
||||
);
|
||||
}
|
||||
return Some(class_literal.default_specialization(db));
|
||||
}
|
||||
|
||||
Some(class_literal.apply_specialization(db, |_| generic_context.specialize(db, types)))
|
||||
Some(to_specialized_class_type_impl(
|
||||
db,
|
||||
self,
|
||||
class_literal,
|
||||
types,
|
||||
generic_context,
|
||||
))
|
||||
}
|
||||
|
||||
/// Lookup a [`KnownClass`] in typeshed and return a [`Type`]
|
||||
|
||||
@@ -3774,10 +3774,7 @@ impl<'db> BoundTypeVarInstance<'db> {
|
||||
/// specifies the required specializations, and the iterator will be empty. For a constrained
|
||||
/// typevar, the primary result will include the fully static constraints, and the iterator
|
||||
/// will include an entry for each non-fully-static constraint.
|
||||
fn required_specializations(
|
||||
self,
|
||||
db: &'db dyn Db,
|
||||
) -> (Node<'db>, impl IntoIterator<Item = Node<'db>>) {
|
||||
fn required_specializations(self, db: &'db dyn Db) -> (Node<'db>, Vec<Node<'db>>) {
|
||||
// For upper bounds and constraints, we are free to choose any materialization that makes
|
||||
// the check succeed. In non-inferable positions, it is most helpful to choose a
|
||||
// materialization that is as restrictive as possible, since that minimizes the number of
|
||||
|
||||
@@ -93,25 +93,31 @@ impl<'db> DisplaySettings<'db> {
|
||||
I: IntoIterator<Item = T>,
|
||||
T: Into<Type<'db>>,
|
||||
{
|
||||
fn build_display_settings<'db>(
|
||||
collector: &AmbiguousClassCollector<'db>,
|
||||
) -> DisplaySettings<'db> {
|
||||
DisplaySettings {
|
||||
qualified: Rc::new(
|
||||
collector
|
||||
.class_names
|
||||
.borrow()
|
||||
.iter()
|
||||
.filter_map(|(name, ambiguity)| {
|
||||
Some((*name, QualificationLevel::from_ambiguity_state(ambiguity)?))
|
||||
})
|
||||
.collect(),
|
||||
),
|
||||
..DisplaySettings::default()
|
||||
}
|
||||
}
|
||||
|
||||
let collector = AmbiguousClassCollector::default();
|
||||
|
||||
for ty in types {
|
||||
collector.visit_type(db, ty.into());
|
||||
}
|
||||
|
||||
Self {
|
||||
qualified: Rc::new(
|
||||
collector
|
||||
.class_names
|
||||
.borrow()
|
||||
.iter()
|
||||
.filter_map(|(name, ambiguity)| {
|
||||
Some((*name, QualificationLevel::from_ambiguity_state(ambiguity)?))
|
||||
})
|
||||
.collect(),
|
||||
),
|
||||
..Self::default()
|
||||
}
|
||||
build_display_settings(&collector)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -563,38 +563,46 @@ impl<'db> GenericContext<'db> {
|
||||
I: IntoIterator<Item = Option<Type<'db>>>,
|
||||
I::IntoIter: ExactSizeIterator,
|
||||
{
|
||||
let mut types = self.fill_in_defaults(db, types);
|
||||
let len = types.len();
|
||||
loop {
|
||||
let mut any_changed = false;
|
||||
for i in 0..len {
|
||||
let partial = PartialSpecialization {
|
||||
generic_context: self,
|
||||
types: &types,
|
||||
// Don't recursively substitute type[i] in itself. Ideally, we could instead
|
||||
// check if the result is self-referential after we're done applying the
|
||||
// partial specialization. But when we apply a paramspec, we don't use the
|
||||
// callable that it maps to directly; we create a new callable that reuses
|
||||
// parts of it. That means we can't look for the previous type directly.
|
||||
// Instead we use this to skip specializing the type in itself in the first
|
||||
// place.
|
||||
skip: Some(i),
|
||||
};
|
||||
let updated = types[i].apply_type_mapping(
|
||||
db,
|
||||
&TypeMapping::PartialSpecialization(partial),
|
||||
TypeContext::default(),
|
||||
);
|
||||
if updated != types[i] {
|
||||
types[i] = updated;
|
||||
any_changed = true;
|
||||
fn specialize_recursive_impl<'db>(
|
||||
db: &'db dyn Db,
|
||||
context: GenericContext<'db>,
|
||||
mut types: Box<[Type<'db>]>,
|
||||
) -> Specialization<'db> {
|
||||
let len = types.len();
|
||||
loop {
|
||||
let mut any_changed = false;
|
||||
for i in 0..len {
|
||||
let partial = PartialSpecialization {
|
||||
generic_context: context,
|
||||
types: &types,
|
||||
// Don't recursively substitute type[i] in itself. Ideally, we could instead
|
||||
// check if the result is self-referential after we're done applying the
|
||||
// partial specialization. But when we apply a paramspec, we don't use the
|
||||
// callable that it maps to directly; we create a new callable that reuses
|
||||
// parts of it. That means we can't look for the previous type directly.
|
||||
// Instead we use this to skip specializing the type in itself in the first
|
||||
// place.
|
||||
skip: Some(i),
|
||||
};
|
||||
let updated = types[i].apply_type_mapping(
|
||||
db,
|
||||
&TypeMapping::PartialSpecialization(partial),
|
||||
TypeContext::default(),
|
||||
);
|
||||
if updated != types[i] {
|
||||
types[i] = updated;
|
||||
any_changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if !any_changed {
|
||||
return Specialization::new(db, context, types, None, None);
|
||||
}
|
||||
}
|
||||
|
||||
if !any_changed {
|
||||
return Specialization::new(db, self, types, None, None);
|
||||
}
|
||||
}
|
||||
|
||||
let types = self.fill_in_defaults(db, types);
|
||||
specialize_recursive_impl(db, self, types)
|
||||
}
|
||||
|
||||
/// Creates a specialization of this generic context for the `tuple` class.
|
||||
@@ -614,7 +622,7 @@ impl<'db> GenericContext<'db> {
|
||||
{
|
||||
let types = types.into_iter();
|
||||
let variables = self.variables(db);
|
||||
assert!(self.len(db) == types.len());
|
||||
assert_eq!(self.len(db), types.len());
|
||||
|
||||
// Typevars can have other typevars as their default values, e.g.
|
||||
//
|
||||
|
||||
@@ -514,7 +514,7 @@ pub fn call_signature_details<'db>(
|
||||
// Extract signature details from all callable bindings
|
||||
bindings
|
||||
.into_iter()
|
||||
.flat_map(std::iter::IntoIterator::into_iter)
|
||||
.flatten()
|
||||
.map(|binding| {
|
||||
let argument_to_parameter_mapping = binding.argument_matches().to_vec();
|
||||
let signature = binding.signature;
|
||||
@@ -623,7 +623,7 @@ pub fn definitions_for_bin_op<'db>(
|
||||
|
||||
let definitions: Vec<_> = bindings
|
||||
.into_iter()
|
||||
.flat_map(std::iter::IntoIterator::into_iter)
|
||||
.flatten()
|
||||
.filter_map(|binding| {
|
||||
Some(ResolvedDefinition::Definition(
|
||||
binding.signature.definition?,
|
||||
@@ -681,7 +681,7 @@ pub fn definitions_for_unary_op<'db>(
|
||||
|
||||
let definitions = bindings
|
||||
.into_iter()
|
||||
.flat_map(std::iter::IntoIterator::into_iter)
|
||||
.flatten()
|
||||
.filter_map(|binding| {
|
||||
Some(ResolvedDefinition::Definition(
|
||||
binding.signature.definition?,
|
||||
|
||||
@@ -1841,32 +1841,37 @@ impl<'db> Parameters<'db> {
|
||||
db: &'db dyn Db,
|
||||
parameters: impl IntoIterator<Item = Parameter<'db>>,
|
||||
) -> Self {
|
||||
let value: Vec<Parameter<'db>> = parameters.into_iter().collect();
|
||||
let mut kind = ParametersKind::Standard;
|
||||
if let [p1, p2] = value.as_slice()
|
||||
&& p1.is_variadic()
|
||||
&& p2.is_keyword_variadic()
|
||||
{
|
||||
match (p1.annotated_type(), p2.annotated_type()) {
|
||||
(None | Some(Type::Dynamic(_)), None | Some(Type::Dynamic(_))) => {
|
||||
kind = ParametersKind::Gradual;
|
||||
}
|
||||
(Some(Type::TypeVar(args_typevar)), Some(Type::TypeVar(kwargs_typevar))) => {
|
||||
if let (Some(ParamSpecAttrKind::Args), Some(ParamSpecAttrKind::Kwargs)) = (
|
||||
args_typevar.paramspec_attr(db),
|
||||
kwargs_typevar.paramspec_attr(db),
|
||||
) {
|
||||
let typevar = args_typevar.without_paramspec_attr(db);
|
||||
if typevar.is_same_typevar_as(db, kwargs_typevar.without_paramspec_attr(db))
|
||||
{
|
||||
kind = ParametersKind::ParamSpec(typevar);
|
||||
fn new_impl<'db>(db: &'db dyn Db, value: Vec<Parameter<'db>>) -> Parameters<'db> {
|
||||
let mut kind = ParametersKind::Standard;
|
||||
if let [p1, p2] = value.as_slice()
|
||||
&& p1.is_variadic()
|
||||
&& p2.is_keyword_variadic()
|
||||
{
|
||||
match (p1.annotated_type(), p2.annotated_type()) {
|
||||
(None | Some(Type::Dynamic(_)), None | Some(Type::Dynamic(_))) => {
|
||||
kind = ParametersKind::Gradual;
|
||||
}
|
||||
(Some(Type::TypeVar(args_typevar)), Some(Type::TypeVar(kwargs_typevar))) => {
|
||||
if let (Some(ParamSpecAttrKind::Args), Some(ParamSpecAttrKind::Kwargs)) = (
|
||||
args_typevar.paramspec_attr(db),
|
||||
kwargs_typevar.paramspec_attr(db),
|
||||
) {
|
||||
let typevar = args_typevar.without_paramspec_attr(db);
|
||||
if typevar
|
||||
.is_same_typevar_as(db, kwargs_typevar.without_paramspec_attr(db))
|
||||
{
|
||||
kind = ParametersKind::ParamSpec(typevar);
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
Parameters { value, kind }
|
||||
}
|
||||
Self { value, kind }
|
||||
|
||||
let value: Vec<Parameter<'db>> = parameters.into_iter().collect();
|
||||
new_impl(db, value)
|
||||
}
|
||||
|
||||
/// Create an empty parameter list.
|
||||
|
||||
Reference in New Issue
Block a user