[core] add module type
This commit is contained in:
parent
f15a64cc1b
commit
ce40a46f8a
@ -205,6 +205,19 @@ impl ConcreteTypeStore {
|
||||
})
|
||||
.collect(),
|
||||
},
|
||||
TypeEnum::TModule { module_id, attributes } => ConcreteTypeEnum::TModule {
|
||||
module_id: *module_id,
|
||||
methods: attributes
|
||||
.iter()
|
||||
.filter_map(|(name, ty)| match &*unifier.get_ty(ty.0) {
|
||||
TypeEnum::TFunc(..) | TypeEnum::TObj { .. } => None,
|
||||
_ => Some((
|
||||
*name,
|
||||
(self.from_unifier_type(unifier, primitives, ty.0, cache), ty.1),
|
||||
)),
|
||||
})
|
||||
.collect(),
|
||||
},
|
||||
TypeEnum::TVirtual { ty } => ConcreteTypeEnum::TVirtual {
|
||||
ty: self.from_unifier_type(unifier, primitives, *ty, cache),
|
||||
},
|
||||
|
@ -2008,72 +2008,90 @@ impl Inferencer<'_> {
|
||||
ctx: ExprContext,
|
||||
) -> InferenceResult {
|
||||
let ty = value.custom.unwrap();
|
||||
if let TypeEnum::TObj { obj_id, fields, .. } = &*self.unifier.get_ty(ty) {
|
||||
// just a fast path
|
||||
match (fields.get(&attr), ctx == ExprContext::Store) {
|
||||
(Some((ty, true)), _) | (Some((ty, false)), false) => Ok(*ty),
|
||||
(Some((ty, false)), true) => report_type_error(
|
||||
TypeErrorKind::MutationError(RecordKey::Str(attr), *ty),
|
||||
Some(value.location),
|
||||
self.unifier,
|
||||
),
|
||||
(None, mutable) => {
|
||||
// Check whether it is a class attribute
|
||||
let defs = self.top_level.definitions.read();
|
||||
let result = {
|
||||
if let TopLevelDef::Class { attributes, .. } = &*defs[obj_id.0].read() {
|
||||
attributes.iter().find_map(|f| {
|
||||
if f.0 == attr {
|
||||
return Some(f.1);
|
||||
}
|
||||
None
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
};
|
||||
match result {
|
||||
Some(res) if !mutable => Ok(res),
|
||||
Some(_) => report_error(
|
||||
&format!("Class Attribute `{attr}` is immutable"),
|
||||
value.location,
|
||||
),
|
||||
None => report_type_error(
|
||||
TypeErrorKind::NoSuchField(RecordKey::Str(attr), ty),
|
||||
Some(value.location),
|
||||
self.unifier,
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if let TypeEnum::TFunc(sign) = &*self.unifier.get_ty(ty) {
|
||||
// Access Class Attributes of classes with __init__ function using Class names e.g. Foo.ATTR1
|
||||
let result = {
|
||||
self.top_level.definitions.read().iter().find_map(|def| {
|
||||
if let Some(rear_guard) = def.try_read() {
|
||||
if let TopLevelDef::Class { name, attributes, .. } = &*rear_guard {
|
||||
if name.to_string() == self.unifier.stringify(sign.ret) {
|
||||
return attributes.iter().find_map(|f| {
|
||||
match &*self.unifier.get_ty(ty) {
|
||||
TypeEnum::TObj { obj_id, fields, .. } => {
|
||||
// just a fast path
|
||||
match (fields.get(&attr), ctx == ExprContext::Store) {
|
||||
(Some((ty, true)), _) | (Some((ty, false)), false) => Ok(*ty),
|
||||
(Some((ty, false)), true) => report_type_error(
|
||||
TypeErrorKind::MutationError(RecordKey::Str(attr), *ty),
|
||||
Some(value.location),
|
||||
self.unifier,
|
||||
),
|
||||
(None, mutable) => {
|
||||
// Check whether it is a class attribute
|
||||
let defs = self.top_level.definitions.read();
|
||||
let result = {
|
||||
if let TopLevelDef::Class { attributes, .. } = &*defs[obj_id.0].read() {
|
||||
attributes.iter().find_map(|f| {
|
||||
if f.0 == attr {
|
||||
return Some(f.clone().1);
|
||||
return Some(f.1);
|
||||
}
|
||||
None
|
||||
});
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
};
|
||||
match result {
|
||||
Some(res) if !mutable => Ok(res),
|
||||
Some(_) => report_error(
|
||||
&format!("Class Attribute `{attr}` is immutable"),
|
||||
value.location,
|
||||
),
|
||||
None => report_type_error(
|
||||
TypeErrorKind::NoSuchField(RecordKey::Str(attr), ty),
|
||||
Some(value.location),
|
||||
self.unifier,
|
||||
),
|
||||
}
|
||||
}
|
||||
None
|
||||
})
|
||||
};
|
||||
match result {
|
||||
Some(f) if ctx != ExprContext::Store => Ok(f),
|
||||
Some(_) => {
|
||||
report_error(&format!("Class Attribute `{attr}` is immutable"), value.location)
|
||||
}
|
||||
None => self.infer_general_attribute(value, attr, ctx),
|
||||
}
|
||||
} else {
|
||||
self.infer_general_attribute(value, attr, ctx)
|
||||
TypeEnum::TFunc(sign) => {
|
||||
// Access Class Attributes of classes with __init__ function using Class names e.g. Foo.ATTR1
|
||||
let result = {
|
||||
self.top_level.definitions.read().iter().find_map(|def| {
|
||||
if let Some(rear_guard) = def.try_read() {
|
||||
if let TopLevelDef::Class { name, attributes, .. } = &*rear_guard {
|
||||
if name.to_string() == self.unifier.stringify(sign.ret) {
|
||||
return attributes.iter().find_map(|f| {
|
||||
if f.0 == attr {
|
||||
return Some(f.clone().1);
|
||||
}
|
||||
None
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
})
|
||||
};
|
||||
match result {
|
||||
Some(f) if ctx != ExprContext::Store => Ok(f),
|
||||
Some(_) => report_error(
|
||||
&format!("Class Attribute `{attr}` is immutable"),
|
||||
value.location,
|
||||
),
|
||||
None => self.infer_general_attribute(value, attr, ctx),
|
||||
}
|
||||
}
|
||||
TypeEnum::TModule { attributes, .. } => {
|
||||
match (attributes.get(&attr), ctx == ExprContext::Load) {
|
||||
(Some((ty, _)), true) | (Some((ty, false)), false) => Ok(*ty),
|
||||
(Some((ty, true)), false) => report_type_error(
|
||||
TypeErrorKind::MutationError(RecordKey::Str(attr), *ty),
|
||||
Some(value.location),
|
||||
self.unifier,
|
||||
),
|
||||
(None, _) => report_type_error(
|
||||
TypeErrorKind::NoSuchField(RecordKey::Str(attr), ty),
|
||||
Some(value.location),
|
||||
self.unifier,
|
||||
),
|
||||
}
|
||||
}
|
||||
_ => self.infer_general_attribute(value, attr, ctx),
|
||||
}
|
||||
}
|
||||
|
||||
@ -2734,8 +2752,7 @@ impl Inferencer<'_> {
|
||||
.read()
|
||||
.iter()
|
||||
.map(|def| match *def.read() {
|
||||
TopLevelDef::Class { name, .. }
|
||||
| TopLevelDef::Module { name, .. } => (name, false),
|
||||
TopLevelDef::Class { name, .. } | TopLevelDef::Module { name, .. } => (name, false),
|
||||
TopLevelDef::Function { simple_name, .. } => (simple_name, false),
|
||||
TopLevelDef::Variable { simple_name, .. } => (simple_name, true),
|
||||
})
|
||||
|
@ -270,6 +270,19 @@ pub enum TypeEnum {
|
||||
|
||||
/// A function type.
|
||||
TFunc(FunSignature),
|
||||
|
||||
/// Module Type
|
||||
TModule {
|
||||
/// The [`DefinitionId`] of this object type.
|
||||
module_id: DefinitionId,
|
||||
|
||||
/// The attributes present in this object type.
|
||||
///
|
||||
/// The key of the [Mapping] is the identifier of the field, while the value is a tuple
|
||||
/// containing the [Type] of the field, and a `bool` indicating whether the field is a
|
||||
/// variable (as opposed to a function).
|
||||
attributes: Mapping<StrRef, (Type, bool)>,
|
||||
},
|
||||
}
|
||||
|
||||
impl TypeEnum {
|
||||
@ -284,6 +297,7 @@ impl TypeEnum {
|
||||
TypeEnum::TVirtual { .. } => "TVirtual",
|
||||
TypeEnum::TCall { .. } => "TCall",
|
||||
TypeEnum::TFunc { .. } => "TFunc",
|
||||
TypeEnum::TModule { .. } => "TModule",
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -593,7 +607,8 @@ impl Unifier {
|
||||
| TLiteral { .. }
|
||||
// functions are instantiated for each call sites, so the function type can contain
|
||||
// type variables.
|
||||
| TFunc { .. } => true,
|
||||
| TFunc { .. }
|
||||
| TModule { .. } => true,
|
||||
|
||||
TVar { .. } => allowed_typevars.iter().any(|b| self.unification_table.unioned(a, *b)),
|
||||
TCall { .. } => false,
|
||||
@ -1315,10 +1330,12 @@ impl Unifier {
|
||||
|| format!("{id}"),
|
||||
|top_level| {
|
||||
let top_level_def = &top_level.definitions.read()[id];
|
||||
let TopLevelDef::Class { name, .. } = &*top_level_def.read() else {
|
||||
unreachable!("expected class definition")
|
||||
let top_level_def = top_level_def.read();
|
||||
let (TopLevelDef::Class { name, .. } | TopLevelDef::Module { name, .. }) =
|
||||
&*top_level_def
|
||||
else {
|
||||
unreachable!("expected module/class definition")
|
||||
};
|
||||
|
||||
name.to_string()
|
||||
},
|
||||
)
|
||||
@ -1446,6 +1463,10 @@ impl Unifier {
|
||||
let ret = self.internal_stringify(signature.ret, obj_to_name, var_to_name, notes);
|
||||
format!("fn[[{params}], {ret}]")
|
||||
}
|
||||
TypeEnum::TModule { module_id, .. } => {
|
||||
let name = obj_to_name(module_id.0);
|
||||
name.to_string()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1521,7 +1542,9 @@ impl Unifier {
|
||||
// variables, i.e. things like TRecord, TCall should not occur, and we
|
||||
// should be safe to not implement the substitution for those variants.
|
||||
match &*ty {
|
||||
TypeEnum::TRigidVar { .. } | TypeEnum::TLiteral { .. } => None,
|
||||
TypeEnum::TRigidVar { .. } | TypeEnum::TLiteral { .. } | TypeEnum::TModule { .. } => {
|
||||
None
|
||||
}
|
||||
TypeEnum::TVar { id, .. } => mapping.get(id).copied(),
|
||||
TypeEnum::TTuple { ty, is_vararg_ctx } => {
|
||||
let mut new_ty = Cow::from(ty);
|
||||
|
Loading…
Reference in New Issue
Block a user