Compare commits

..

No commits in common. "refactor_composer" and "master" have entirely different histories.

6 changed files with 679 additions and 442 deletions

View File

@ -1,6 +1,5 @@
use std::rc::Rc; use std::rc::Rc;
use indexmap::IndexMap;
use nac3parser::ast::{fold::Fold, ExprKind, Ident}; use nac3parser::ast::{fold::Fold, ExprKind, Ident};
use super::*; use super::*;
@ -464,9 +463,9 @@ impl TopLevelComposer {
Ok((name, DefinitionId(self.definition_ast_list.len() - 1), Some(ty_to_be_unified))) Ok((name, DefinitionId(self.definition_ast_list.len() - 1), Some(ty_to_be_unified)))
} }
/// Analyze the AST and modify the corresponding `TopLevelDef`
pub fn start_analysis(&mut self, inference: bool) -> Result<(), HashSet<String>> { pub fn start_analysis(&mut self, inference: bool) -> Result<(), HashSet<String>> {
self.analyze_top_level_class_definition()?; self.analyze_top_level_class_type_var()?;
self.analyze_top_level_class_bases()?;
self.analyze_top_level_class_fields_methods()?; self.analyze_top_level_class_fields_methods()?;
self.analyze_top_level_function()?; self.analyze_top_level_function()?;
if inference { if inference {
@ -476,184 +475,442 @@ impl TopLevelComposer {
Ok(()) Ok(())
} }
/// step 1, analyze the top level class definitions /// step 1, analyze the type vars associated with top level class
/// fn analyze_top_level_class_type_var(&mut self) -> Result<(), HashSet<String>> {
/// Checks for class type variables and ancestors adding them to the `TopLevelDef` list
fn analyze_top_level_class_definition(&mut self) -> Result<(), HashSet<String>> {
let def_list = &self.definition_ast_list; let def_list = &self.definition_ast_list;
let temp_def_list = self.extract_def_list();
let unifier = self.unifier.borrow_mut(); let unifier = self.unifier.borrow_mut();
let primitives_store = &self.primitives_ty; let primitives_store = &self.primitives_ty;
let mut errors = HashSet::new();
// Initially only copy the definitions of buitin classes and functions let mut analyze = |class_def: &Arc<RwLock<TopLevelDef>>, class_ast: &Option<Stmt>| {
// class definitions are added in the same order as they appear in the program // only deal with class def here
let mut temp_def_list: Vec<Arc<RwLock<TopLevelDef>>> = let mut class_def = class_def.write();
def_list.iter().take(self.builtin_num).map(|f| f.0.clone()).collect_vec(); let (class_bases_ast, class_def_type_vars, class_resolver) = {
if let TopLevelDef::Class { type_vars, resolver, .. } = &mut *class_def {
// Check for class generic variables and ancestors let Some(ast::Located { node: ast::StmtKind::ClassDef { bases, .. }, .. }) =
for (class_def, class_ast) in def_list.iter().skip(self.builtin_num) { class_ast
if class_ast.is_some() && matches!(&*class_def.read(), TopLevelDef::Class { .. }) {
// Add class type variables and direct parents to the `TopLevelDef`
if let Err(e) = Self::analyze_class_bases(
class_def,
class_ast,
&temp_def_list,
unifier,
primitives_store,
) {
errors.extend(e);
}
// Add class ancestors
Self::analyze_class_ancestors(class_def, &temp_def_list);
// special case classes that inherit from Exception
let TopLevelDef::Class { ancestors: class_ancestors, .. } = &*class_def.read()
else {
unreachable!()
};
if class_ancestors
.iter()
.any(|ann| matches!(ann, TypeAnnotation::CustomClass { id, .. } if id.0 == 7))
{
// if inherited from Exception, the body should be a pass
let ast::StmtKind::ClassDef { body, .. } = &class_ast.as_ref().unwrap().node
else { else {
unreachable!() unreachable!()
}; };
for stmt in body {
if matches!( (bases, type_vars, resolver)
stmt.node, } else {
ast::StmtKind::FunctionDef { .. } | ast::StmtKind::AnnAssign { .. } return Ok(());
) { }
errors.extend(Err(HashSet::from(["Classes inherited from exception should have no custom fields/methods"]))); };
let class_resolver = class_resolver.as_ref().unwrap();
let class_resolver = &**class_resolver;
let mut is_generic = false;
for b in class_bases_ast {
match &b.node {
// analyze typevars bounded to the class,
// only support things like `class A(Generic[T, V])`,
// things like `class A(Generic[T, V, ImportedModule.T])` is not supported
// i.e. only simple names are allowed in the subscript
// should update the TopLevelDef::Class.typevars and the TypeEnum::TObj.params
ExprKind::Subscript { value, slice, .. }
if {
matches!(
&value.node,
ast::ExprKind::Name { id, .. } if id == &"Generic".into()
)
} =>
{
if is_generic {
return Err(HashSet::from([format!(
"only single Generic[...] is allowed (at {})",
b.location
)]));
} }
is_generic = true;
let type_var_list: Vec<&Expr<()>>;
// if `class A(Generic[T, V, G])`
if let ExprKind::Tuple { elts, .. } = &slice.node {
type_var_list = elts.iter().collect_vec();
// `class A(Generic[T])`
} else {
type_var_list = vec![&**slice];
}
// parse the type vars
let type_vars = type_var_list
.into_iter()
.map(|e| {
class_resolver.parse_type_annotation(
&temp_def_list,
unifier,
primitives_store,
e,
)
})
.collect::<Result<Vec<_>, _>>()?;
// check if all are unique type vars
let all_unique_type_var = {
let mut occurred_type_var_id: HashSet<TypeVarId> = HashSet::new();
type_vars.iter().all(|x| {
let ty = unifier.get_ty(*x);
if let TypeEnum::TVar { id, .. } = ty.as_ref() {
occurred_type_var_id.insert(*id)
} else {
false
}
})
};
if !all_unique_type_var {
return Err(HashSet::from([format!(
"duplicate type variable occurs (at {})",
slice.location
)]));
}
// add to TopLevelDef
class_def_type_vars.extend(type_vars);
} }
// if others, do nothing in this function
_ => continue,
} }
} }
temp_def_list.push(class_def.clone()); Ok(())
}
// deal with ancestors of Exception object
let TopLevelDef::Class { name, ancestors, object_id, .. } = &mut *def_list[7].0.write()
else {
unreachable!()
}; };
assert_eq!(*name, "Exception".into());
ancestors.push(make_self_type_annotation(&[], *object_id));
let mut errors = HashSet::new();
for (class_def, class_ast) in def_list.iter().skip(self.builtin_num) {
if class_ast.is_none() {
continue;
}
if let Err(e) = analyze(class_def, class_ast) {
errors.extend(e);
}
}
if !errors.is_empty() { if !errors.is_empty() {
return Err(errors); return Err(errors);
} }
Ok(()) Ok(())
} }
/// step 2, class fields and methods /// step 2, base classes.
fn analyze_top_level_class_fields_methods(&mut self) -> Result<(), HashSet<String>> { /// now that the type vars of all classes are done, handle base classes and
// Allow resolving definition IDs in error messages /// put Self class into the ancestors list. We only allow single inheritance
fn analyze_top_level_class_bases(&mut self) -> Result<(), HashSet<String>> {
if self.unifier.top_level.is_none() { if self.unifier.top_level.is_none() {
let ctx = Arc::new(self.make_top_level_context()); let ctx = Arc::new(self.make_top_level_context());
self.unifier.top_level = Some(ctx); self.unifier.top_level = Some(ctx);
} }
let def_list = &self.definition_ast_list;
let temp_def_list = self.extract_def_list(); let temp_def_list = self.extract_def_list();
let unifier = self.unifier.borrow_mut(); let unifier = self.unifier.borrow_mut();
let primitives_store = &self.primitives_ty; let primitive_types = self.primitives_ty;
let mut errors: HashSet<String> = HashSet::new(); let mut get_direct_parents =
let mut type_var_to_concrete_def: HashMap<Type, TypeAnnotation> = HashMap::new(); |class_def: &Arc<RwLock<TopLevelDef>>, class_ast: &Option<Stmt>| {
let mut class_def = class_def.write();
for (class_def, class_ast) in def_list.iter().skip(self.builtin_num) { let (class_def_id, class_bases, class_ancestors, class_resolver, class_type_vars) = {
if class_ast.is_some() && matches!(&*class_def.read(), TopLevelDef::Class { .. }) { if let TopLevelDef::Class {
if let Err(e) = Self::analyze_single_class_methods_fields( ancestors, resolver, object_id, type_vars, ..
class_def, } = &mut *class_def
&class_ast.as_ref().unwrap().node, {
&temp_def_list, let Some(ast::Located {
unifier, node: ast::StmtKind::ClassDef { bases, .. }, ..
primitives_store, }) = class_ast
&mut type_var_to_concrete_def, else {
(&self.keyword_list, &self.core_config), unreachable!()
) {
errors.extend(e);
}
// The errors need to be reported before copying methods from parent to child classes
if !errors.is_empty() {
return Err(errors);
}
// The lock on `class_def` must be released once the ancestors are updated
{
let mut class_def = class_def.write();
let TopLevelDef::Class { ancestors, .. } = &*class_def else { unreachable!() };
// Methods/fields needs to be processed only if class inherits from another class
if ancestors.len() > 1 {
if let Err(e) = Self::analyze_single_class_ancestors(
&mut class_def,
&temp_def_list,
unifier,
primitives_store,
&mut type_var_to_concrete_def,
) {
errors.extend(e);
}; };
}
}
let mut subst_list = Some(Vec::new()); (object_id, bases, ancestors, resolver, type_vars)
// unification of previously assigned typevar } else {
let mut unification_helper = |ty, def| -> Result<(), HashSet<String>> { return Ok(());
let target_ty = get_type_from_type_annotation_kinds( }
};
let class_resolver = class_resolver.as_ref().unwrap();
let class_resolver = &**class_resolver;
let mut has_base = false;
for b in class_bases {
// type vars have already been handled, so skip on `Generic[...]`
if matches!(
&b.node,
ast::ExprKind::Subscript { value, .. }
if matches!(
&value.node,
ast::ExprKind::Name { id, .. } if id == &"Generic".into()
)
) {
continue;
}
if has_base {
return Err(HashSet::from([format!(
"a class definition can only have at most one base class \
declaration and one generic declaration (at {})",
b.location
)]));
}
has_base = true;
// the function parse_ast_to make sure that no type var occurred in
// bast_ty if it is a CustomClassKind
let base_ty = parse_ast_to_type_annotation_kinds(
class_resolver,
&temp_def_list, &temp_def_list,
unifier, unifier,
primitives_store, &primitive_types,
&def, b,
&mut subst_list, vec![(*class_def_id, class_type_vars.clone())]
.into_iter()
.collect::<HashMap<_, _>>(),
)?; )?;
unifier
.unify(ty, target_ty) if let TypeAnnotation::CustomClass { .. } = &base_ty {
.map_err(|e| HashSet::from([e.to_display(unifier).to_string()]))?; class_ancestors.push(base_ty);
Ok(()) } else {
}; return Err(HashSet::from([format!(
for (ty, def) in &type_var_to_concrete_def { "class base declaration can only be custom class (at {})",
if let Err(e) = unification_helper(*ty, def.clone()) { b.location,
errors.extend(e); )]));
} }
} }
for ty in subst_list.unwrap() { Ok(())
let TypeEnum::TObj { obj_id, params, fields } = &*unifier.get_ty(ty) else { };
unreachable!()
};
let mut new_fields = HashMap::new(); // first, only push direct parent into the list
let mut need_subst = false; let mut errors = HashSet::new();
for (name, (ty, mutable)) in fields { for (class_def, class_ast) in self.definition_ast_list.iter_mut().skip(self.builtin_num) {
let substituted = unifier.subst(*ty, params); if class_ast.is_none() {
need_subst |= substituted.is_some(); continue;
new_fields.insert(*name, (substituted.unwrap_or(*ty), *mutable)); }
if let Err(e) = get_direct_parents(class_def, class_ast) {
errors.extend(e);
}
}
if !errors.is_empty() {
return Err(errors);
}
// second, get all ancestors
let mut ancestors_store: HashMap<DefinitionId, Vec<TypeAnnotation>> = HashMap::default();
let mut get_all_ancestors =
|class_def: &Arc<RwLock<TopLevelDef>>| -> Result<(), HashSet<String>> {
let class_def = class_def.read();
let (class_ancestors, class_id) = {
if let TopLevelDef::Class { ancestors, object_id, .. } = &*class_def {
(ancestors, *object_id)
} else {
return Ok(());
} }
if need_subst { };
let new_ty = unifier.add_ty(TypeEnum::TObj { ancestors_store.insert(
obj_id: *obj_id, class_id,
params: params.clone(), // if class has direct parents, get all ancestors of its parents. Else just empty
fields: new_fields, if class_ancestors.is_empty() {
}); vec![]
if let Err(e) = unifier.unify(ty, new_ty) { } else {
errors.insert(e.to_display(unifier).to_string()); Self::get_all_ancestors_helper(
} &class_ancestors[0],
temp_def_list.as_slice(),
)?
},
);
Ok(())
};
for (class_def, ast) in self.definition_ast_list.iter().skip(self.builtin_num) {
if ast.is_none() {
continue;
}
if let Err(e) = get_all_ancestors(class_def) {
errors.extend(e);
}
}
if !errors.is_empty() {
return Err(errors);
}
// insert the ancestors to the def list
for (class_def, class_ast) in self.definition_ast_list.iter_mut().skip(self.builtin_num) {
if class_ast.is_none() {
continue;
}
let mut class_def = class_def.write();
let (class_ancestors, class_id, class_type_vars) = {
if let TopLevelDef::Class { ancestors, object_id, type_vars, .. } = &mut *class_def
{
(ancestors, *object_id, type_vars)
} else {
continue;
}
};
let ans = ancestors_store.get_mut(&class_id).unwrap();
class_ancestors.append(ans);
// insert self type annotation to the front of the vector to maintain the order
class_ancestors
.insert(0, make_self_type_annotation(class_type_vars.as_slice(), class_id));
// special case classes that inherit from Exception
if class_ancestors
.iter()
.any(|ann| matches!(ann, TypeAnnotation::CustomClass { id, .. } if id.0 == 7))
{
// if inherited from Exception, the body should be a pass
let ast::StmtKind::ClassDef { body, .. } = &class_ast.as_ref().unwrap().node else {
unreachable!()
};
for stmt in body {
if matches!(
stmt.node,
ast::StmtKind::FunctionDef { .. } | ast::StmtKind::AnnAssign { .. }
) {
return Err(HashSet::from([
"Classes inherited from exception should have no custom fields/methods"
.into(),
]));
} }
} }
} }
} }
for (def, _) in def_list.iter().skip(self.builtin_num) { // deal with ancestor of Exception object
let TopLevelDef::Class { name, ancestors, object_id, .. } =
&mut *self.definition_ast_list[7].0.write()
else {
unreachable!()
};
assert_eq!(*name, "Exception".into());
ancestors.push(make_self_type_annotation(&[], *object_id));
Ok(())
}
/// step 3, class fields and methods
fn analyze_top_level_class_fields_methods(&mut self) -> Result<(), HashSet<String>> {
let temp_def_list = self.extract_def_list();
let primitives = &self.primitives_ty;
let def_ast_list = &self.definition_ast_list;
let unifier = self.unifier.borrow_mut();
let mut type_var_to_concrete_def: HashMap<Type, TypeAnnotation> = HashMap::new();
let mut errors = HashSet::new();
for (class_def, class_ast) in def_ast_list.iter().skip(self.builtin_num) {
if class_ast.is_none() {
continue;
}
if matches!(&*class_def.read(), TopLevelDef::Class { .. }) {
if let Err(e) = Self::analyze_single_class_methods_fields(
class_def,
&class_ast.as_ref().unwrap().node,
&temp_def_list,
unifier,
primitives,
&mut type_var_to_concrete_def,
(&self.keyword_list, &self.core_config),
) {
errors.extend(e);
}
}
}
if !errors.is_empty() {
return Err(errors);
}
// handle the inherited methods and fields
// Note: we cannot defer error handling til the end of the loop, because there is loop
// carried dependency, ignoring the error (temporarily) will cause all assumptions to break
// and produce weird error messages
let mut current_ancestor_depth: usize = 2;
loop {
let mut finished = true;
for (class_def, class_ast) in def_ast_list.iter().skip(self.builtin_num) {
if class_ast.is_none() {
continue;
}
let mut class_def = class_def.write();
if let TopLevelDef::Class { ancestors, .. } = &*class_def {
// if the length of the ancestor is equal to the current depth
// it means that all the ancestors of the class is handled
if ancestors.len() == current_ancestor_depth {
finished = false;
Self::analyze_single_class_ancestors(
&mut class_def,
&temp_def_list,
unifier,
primitives,
&mut type_var_to_concrete_def,
)?;
}
}
}
if finished {
break;
}
current_ancestor_depth += 1;
if current_ancestor_depth > def_ast_list.len() + 1 {
unreachable!("cannot be longer than the whole top level def list")
}
}
let mut subst_list = Some(Vec::new());
// unification of previously assigned typevar
let mut unification_helper = |ty, def| -> Result<(), HashSet<String>> {
let target_ty = get_type_from_type_annotation_kinds(
&temp_def_list,
unifier,
primitives,
&def,
&mut subst_list,
)?;
unifier
.unify(ty, target_ty)
.map_err(|e| HashSet::from([e.to_display(unifier).to_string()]))?;
Ok(())
};
for (ty, def) in type_var_to_concrete_def {
if let Err(e) = unification_helper(ty, def) {
errors.extend(e);
}
}
for ty in subst_list.unwrap() {
let TypeEnum::TObj { obj_id, params, fields } = &*unifier.get_ty(ty) else {
unreachable!()
};
let mut new_fields = HashMap::new();
let mut need_subst = false;
for (name, (ty, mutable)) in fields {
let substituted = unifier.subst(*ty, params);
need_subst |= substituted.is_some();
new_fields.insert(*name, (substituted.unwrap_or(*ty), *mutable));
}
if need_subst {
let new_ty = unifier.add_ty(TypeEnum::TObj {
obj_id: *obj_id,
params: params.clone(),
fields: new_fields,
});
if let Err(e) = unifier.unify(ty, new_ty) {
errors.insert(e.to_display(unifier).to_string());
}
}
}
if !errors.is_empty() {
return Err(errors);
}
for (def, _) in def_ast_list.iter().skip(self.builtin_num) {
match &*def.read() { match &*def.read() {
TopLevelDef::Class { resolver: Some(resolver), .. } TopLevelDef::Class { resolver: Some(resolver), .. }
| TopLevelDef::Function { resolver: Some(resolver), .. } => { | TopLevelDef::Function { resolver: Some(resolver), .. } => {
if let Err(e) = if let Err(e) =
resolver.handle_deferred_eval(unifier, &temp_def_list, primitives_store) resolver.handle_deferred_eval(unifier, &temp_def_list, primitives)
{ {
errors.insert(e); errors.insert(e);
} }
@ -662,13 +919,10 @@ impl TopLevelComposer {
} }
} }
if !errors.is_empty() {
return Err(errors);
}
Ok(()) Ok(())
} }
/// step 3, after class methods are done, top level functions have nothing unknown /// step 4, after class methods are done, top level functions have nothing unknown
fn analyze_top_level_function(&mut self) -> Result<(), HashSet<String>> { fn analyze_top_level_function(&mut self) -> Result<(), HashSet<String>> {
let def_list = &self.definition_ast_list; let def_list = &self.definition_ast_list;
let keyword_list = &self.keyword_list; let keyword_list = &self.keyword_list;
@ -1023,83 +1277,126 @@ impl TopLevelComposer {
let mut method_var_map = VarMap::new(); let mut method_var_map = VarMap::new();
let arg_types: Vec<FuncArg> = { let arg_types: Vec<FuncArg> = {
// Function arguments must have: // check method parameters cannot have same name
// 1) `self` as first argument (we currently do not support staticmethods)
// 2) unique names
// 3) names different than keywords
match args.args.first() {
Some(id) if id.node.arg == "self".into() => {},
_ => return Err(HashSet::from([format!(
"{name} method must have a `self` parameter (at {})", b.location
)])),
}
let mut defined_parameter_name: HashSet<_> = HashSet::new(); let mut defined_parameter_name: HashSet<_> = HashSet::new();
for arg in args.args.iter().skip(1) { let zelf: StrRef = "self".into();
if !defined_parameter_name.insert(arg.node.arg) { for x in &args.args {
return Err(HashSet::from([format!("class method must have a unique parameter names (at {})", b.location)])); if !defined_parameter_name.insert(x.node.arg)
} || (keyword_list.contains(&x.node.arg) && x.node.arg != zelf)
if keyword_list.contains(&arg.node.arg) { {
return Err(HashSet::from([format!("parameter names should not be the same as the keywords (at {})", b.location)])); return Err(HashSet::from([
format!("top level function must have unique parameter names \
and names should not be the same as the keywords (at {})",
x.location),
]))
} }
} }
// `self` must not be provided type annotation or default value if name == &"__init__".into() && !defined_parameter_name.contains(&zelf) {
if args.args.len() == args.defaults.len() { return Err(HashSet::from([
return Err(HashSet::from([format!("`self` cannot have a default value (at {})", b.location)])); format!("__init__ method must have a `self` parameter (at {})", b.location),
]))
} }
if args.args[0].node.annotation.is_some() { if !defined_parameter_name.contains(&zelf) {
return Err(HashSet::from([format!("`self` cannot have a type annotation (at {})", b.location)])); return Err(HashSet::from([
format!("class method must have a `self` parameter (at {})", b.location),
]))
} }
let mut result = Vec::new(); let mut result = Vec::new();
let no_defaults = args.args.len() - args.defaults.len() - 1;
for (idx, x) in itertools::enumerate(args.args.iter().skip(1)) { let arg_with_default: Vec<(
let type_ann = { &ast::Located<ast::ArgData<()>>,
let Some(annotation_expr) = x.node.annotation.as_ref() else {return Err(HashSet::from([format!("type annotation needed for `{}` (at {})", x.node.arg, x.location)]));}; Option<&Expr>,
parse_ast_to_type_annotation_kinds( )> = args
class_resolver, .args
temp_def_list, .iter()
unifier, .rev()
primitives, .zip(
annotation_expr, args.defaults
vec![(class_id, class_type_vars_def.clone())] .iter()
.into_iter() .rev()
.collect::<HashMap<_, _>>(), .map(|x| -> Option<&Expr> { Some(x) })
)? .chain(std::iter::repeat(None)),
}; )
// find type vars within this method parameter type annotation .collect_vec();
let type_vars_within = get_type_var_contained_in_type_annotation(&type_ann);
// handle the class type var and the method type var for (x, default) in arg_with_default.into_iter().rev() {
for type_var_within in type_vars_within { let name = x.node.arg;
let TypeAnnotation::TypeVar(ty) = type_var_within else { if name != zelf {
unreachable!("must be type var annotation") let type_ann = {
let annotation_expr = x
.node
.annotation
.as_ref()
.ok_or_else(|| HashSet::from([
format!(
"type annotation needed for `{}` at {}",
x.node.arg, x.location
),
]))?
.as_ref();
parse_ast_to_type_annotation_kinds(
class_resolver,
temp_def_list,
unifier,
primitives,
annotation_expr,
vec![(class_id, class_type_vars_def.clone())]
.into_iter()
.collect::<HashMap<_, _>>(),
)?
}; };
// find type vars within this method parameter type annotation
let type_vars_within =
get_type_var_contained_in_type_annotation(&type_ann);
// handle the class type var and the method type var
for type_var_within in type_vars_within {
let TypeAnnotation::TypeVar(ty) = type_var_within else {
unreachable!("must be type var annotation")
};
let id = Self::get_var_id(ty, unifier)?; let id = Self::get_var_id(ty, unifier)?;
if let Some(prev_ty) = method_var_map.insert(id, ty) { if let Some(prev_ty) = method_var_map.insert(id, ty) {
// if already in the list, make sure they are the same? // if already in the list, make sure they are the same?
assert_eq!(prev_ty, ty); assert_eq!(prev_ty, ty);
}
} }
// finish handling type vars
let dummy_func_arg = FuncArg {
name,
ty: unifier.get_dummy_var().ty,
default_value: match default {
None => None,
Some(default) => {
if name == "self".into() {
return Err(HashSet::from([
format!("`self` parameter cannot take default value (at {})", x.location),
]));
}
Some({
let v = Self::parse_parameter_default_value(
default,
class_resolver,
)?;
Self::check_default_param_type(
&v, &type_ann, primitives, unifier,
)
.map_err(|err| HashSet::from([
format!("{} (at {})", err, x.location),
]))?;
v
})
}
},
is_vararg: false,
};
// push the dummy type and the type annotation
// into the list for later unification
type_var_to_concrete_def
.insert(dummy_func_arg.ty, type_ann.clone());
result.push(dummy_func_arg);
} }
// finish handling type vars
let dummy_func_arg = FuncArg {
name: x.node.arg,
ty: unifier.get_dummy_var().ty,
default_value: if idx < no_defaults { None } else {
let default_idx = idx - no_defaults;
Some({
let v = Self::parse_parameter_default_value(&args.defaults[default_idx], class_resolver)?;
Self::check_default_param_type(&v, &type_ann, primitives, unifier).map_err(|err| HashSet::from([format!("{} (at {})", err, x.location)]))?;
v
})
},
is_vararg: false,
};
// push the dummy type and the type annotation
// into the list for later unification
type_var_to_concrete_def
.insert(dummy_func_arg.ty, type_ann.clone());
result.push(dummy_func_arg);
} }
result result
}; };
@ -1221,12 +1518,12 @@ impl TopLevelComposer {
match v { match v {
ast::Constant::Bool(_) | ast::Constant::Str(_) | ast::Constant::Int(_) | ast::Constant::Float(_) => {} ast::Constant::Bool(_) | ast::Constant::Str(_) | ast::Constant::Int(_) | ast::Constant::Float(_) => {}
_ => { _ => {
return Err(HashSet::from([ return Err(HashSet::from([
format!( format!(
"unsupported statement in class definition body (at {})", "unsupported statement in class definition body (at {})",
b.location b.location
), ),
])) ]))
} }
} }
class_attributes_def.push((*attr, dummy_field_type, v.clone())); class_attributes_def.push((*attr, dummy_field_type, v.clone()));
@ -1262,7 +1559,7 @@ impl TopLevelComposer {
unreachable!("must be type var annotation") unreachable!("must be type var annotation")
}; };
if !class_type_vars_def.contains(&t){ if !class_type_vars_def.contains(&t) {
return Err(HashSet::from([ return Err(HashSet::from([
format!( format!(
"class fields can only use type \ "class fields can only use type \
@ -1296,7 +1593,7 @@ impl TopLevelComposer {
_ => { _ => {
return Err(HashSet::from([ return Err(HashSet::from([
format!( format!(
"unsupported statement type in class definition body (at {})", "unsupported statement in class definition body (at {})",
b.location b.location
), ),
])) ]))
@ -1342,6 +1639,7 @@ impl TopLevelComposer {
let TypeAnnotation::CustomClass { id, params: _ } = base else { let TypeAnnotation::CustomClass { id, params: _ } = base else {
unreachable!("must be class type annotation") unreachable!("must be class type annotation")
}; };
let base = temp_def_list.get(id.0).unwrap(); let base = temp_def_list.get(id.0).unwrap();
let base = base.read(); let base = base.read();
let TopLevelDef::Class { methods, fields, attributes, .. } = &*base else { let TopLevelDef::Class { methods, fields, attributes, .. } = &*base else {
@ -1350,68 +1648,93 @@ impl TopLevelComposer {
// handle methods override // handle methods override
// since we need to maintain the order, create a new list // since we need to maintain the order, create a new list
let mut new_child_methods: IndexMap<StrRef, (Type, DefinitionId)> = let mut new_child_methods: Vec<(StrRef, Type, DefinitionId)> = Vec::new();
methods.iter().map(|m| (m.0, (m.1, m.2))).collect(); let mut is_override: HashSet<StrRef> = HashSet::new();
for (anc_method_name, anc_method_ty, anc_method_def_id) in methods {
// let mut new_child_methods: Vec<(StrRef, Type, DefinitionId)> = methods.clone(); // find if there is a method with same name in the child class
for (class_method_name, class_method_ty, class_method_defid) in &*class_methods_def { let mut to_be_added = (*anc_method_name, *anc_method_ty, *anc_method_def_id);
if let Some((ty, _)) = new_child_methods for (class_method_name, class_method_ty, class_method_defid) in &*class_methods_def {
.insert(*class_method_name, (*class_method_ty, *class_method_defid)) if class_method_name == anc_method_name {
{ // ignore and handle self
let ok = class_method_name == &"__init__".into() // if is __init__ method, no need to check return type
|| Self::check_overload_function_type( let ok = class_method_name == &"__init__".into()
*class_method_ty, || Self::check_overload_function_type(
ty, *class_method_ty,
unifier, *anc_method_ty,
type_var_to_concrete_def, unifier,
); type_var_to_concrete_def,
if !ok { );
return Err(HashSet::from([format!( if !ok {
"method {class_method_name} has same name as ancestors' method, but incompatible type"), return Err(HashSet::from([format!(
])); "method {class_method_name} has same name as ancestors' method, but incompatible type"),
]));
}
// mark it as added
is_override.insert(*class_method_name);
to_be_added = (*class_method_name, *class_method_ty, *class_method_defid);
break;
} }
} }
new_child_methods.push(to_be_added);
} }
// add those that are not overriding method to the new_child_methods
for (class_method_name, class_method_ty, class_method_defid) in &*class_methods_def {
if !is_override.contains(class_method_name) {
new_child_methods.push((*class_method_name, *class_method_ty, *class_method_defid));
}
}
// use the new_child_methods to replace all the elements in `class_methods_def`
class_methods_def.clear(); class_methods_def.clear();
class_methods_def class_methods_def.extend(new_child_methods);
.extend(new_child_methods.iter().map(|f| (*f.0, f.1 .0, f.1 .1)).collect_vec());
// handle class fields // handle class fields
let mut new_child_fields: IndexMap<StrRef, (Type, bool)> = let mut new_child_fields: Vec<(StrRef, Type, bool)> = Vec::new();
fields.iter().map(|f| (f.0, (f.1, f.2))).collect(); // let mut is_override: HashSet<_> = HashSet::new();
let mut new_child_attributes: IndexMap<StrRef, (Type, ast::Constant)> = for (anc_field_name, anc_field_ty, mutable) in fields {
attributes.iter().map(|f| (f.0, (f.1, f.2.clone()))).collect(); let to_be_added = (*anc_field_name, *anc_field_ty, *mutable);
// Overriding class fields and attributes is currently not supported // find if there is a fields with the same name in the child class
for (name, ty, mutable) in &*class_fields_def { for (class_field_name, ..) in &*class_fields_def {
if new_child_fields.insert(*name, (*ty, *mutable)).is_some() if class_field_name == anc_field_name
|| new_child_attributes.contains_key(name) || attributes.iter().any(|f| f.0 == *class_field_name)
{ {
return Err(HashSet::from([format!( return Err(HashSet::from([format!(
"field `{name}` has already declared in the ancestor classes" "field `{class_field_name}` has already declared in the ancestor classes"
)])); )]));
} }
}
for (name, ty, val) in &*class_attribute_def {
if new_child_attributes.insert(*name, (*ty, val.clone())).is_some()
|| new_child_fields.contains_key(name)
{
return Err(HashSet::from([format!(
"attribute `{name}` has already declared in the ancestor classes"
)]));
} }
new_child_fields.push(to_be_added);
} }
// handle class attributes
let mut new_child_attributes: Vec<(StrRef, Type, ast::Constant)> = Vec::new();
for (anc_attr_name, anc_attr_ty, attr_value) in attributes {
let to_be_added = (*anc_attr_name, *anc_attr_ty, attr_value.clone());
// find if there is a attribute with the same name in the child class
for (class_attr_name, ..) in &*class_attribute_def {
if class_attr_name == anc_attr_name
|| fields.iter().any(|f| f.0 == *class_attr_name)
{
return Err(HashSet::from([format!(
"attribute `{class_attr_name}` has already declared in the ancestor classes"
)]));
}
}
new_child_attributes.push(to_be_added);
}
for (class_field_name, class_field_ty, mutable) in &*class_fields_def {
if !is_override.contains(class_field_name) {
new_child_fields.push((*class_field_name, *class_field_ty, *mutable));
}
}
class_fields_def.clear(); class_fields_def.clear();
class_fields_def class_fields_def.extend(new_child_fields);
.extend(new_child_fields.iter().map(|f| (*f.0, f.1 .0, f.1 .1)).collect_vec());
class_attribute_def.clear(); class_attribute_def.clear();
class_attribute_def.extend( class_attribute_def.extend(new_child_attributes);
new_child_attributes.iter().map(|f| (*f.0, f.1 .0, f.1 .1.clone())).collect_vec(),
);
Ok(()) Ok(())
} }
/// step 4, analyze and call type inferencer to fill the `instance_to_stmt` of /// step 5, analyze and call type inferencer to fill the `instance_to_stmt` of
/// [`TopLevelDef::Function`] /// [`TopLevelDef::Function`]
fn analyze_function_instance(&mut self) -> Result<(), HashSet<String>> { fn analyze_function_instance(&mut self) -> Result<(), HashSet<String>> {
// first get the class constructor type correct for the following type check in function body // first get the class constructor type correct for the following type check in function body
@ -1942,7 +2265,7 @@ impl TopLevelComposer {
Ok(()) Ok(())
} }
/// Step 5. Analyze and populate the types of global variables. /// Step 6. Analyze and populate the types of global variables.
fn analyze_top_level_variables(&mut self) -> Result<(), HashSet<String>> { fn analyze_top_level_variables(&mut self) -> Result<(), HashSet<String>> {
let def_list = &self.definition_ast_list; let def_list = &self.definition_ast_list;
let temp_def_list = self.extract_def_list(); let temp_def_list = self.extract_def_list();

View File

@ -624,6 +624,64 @@ impl TopLevelComposer {
Err(HashSet::from([format!("no method {method_name} in the current class")])) Err(HashSet::from([format!("no method {method_name} in the current class")]))
} }
/// get all base class def id of a class, excluding itself. \
/// this function should called only after the direct parent is set
/// and before all the ancestors are set
/// and when we allow single inheritance \
/// the order of the returned list is from the child to the deepest ancestor
pub fn get_all_ancestors_helper(
child: &TypeAnnotation,
temp_def_list: &[Arc<RwLock<TopLevelDef>>],
) -> Result<Vec<TypeAnnotation>, HashSet<String>> {
let mut result: Vec<TypeAnnotation> = Vec::new();
let mut parent = Self::get_parent(child, temp_def_list);
while let Some(p) = parent {
parent = Self::get_parent(&p, temp_def_list);
let p_id = if let TypeAnnotation::CustomClass { id, .. } = &p {
*id
} else {
unreachable!("must be class kind annotation")
};
// check cycle
let no_cycle = result.iter().all(|x| {
let TypeAnnotation::CustomClass { id, .. } = x else {
unreachable!("must be class kind annotation")
};
id.0 != p_id.0
});
if no_cycle {
result.push(p);
} else {
return Err(HashSet::from(["cyclic inheritance detected".into()]));
}
}
Ok(result)
}
/// should only be called when finding all ancestors, so panic when wrong
fn get_parent(
child: &TypeAnnotation,
temp_def_list: &[Arc<RwLock<TopLevelDef>>],
) -> Option<TypeAnnotation> {
let child_id = if let TypeAnnotation::CustomClass { id, .. } = child {
*id
} else {
unreachable!("should be class type annotation")
};
let child_def = temp_def_list.get(child_id.0).unwrap();
let child_def = child_def.read();
let TopLevelDef::Class { ancestors, .. } = &*child_def else {
unreachable!("child must be top level class def")
};
if ancestors.is_empty() {
None
} else {
Some(ancestors[0].clone())
}
}
/// get the `var_id` of a given `TVar` type /// get the `var_id` of a given `TVar` type
pub fn get_var_id(var_ty: Type, unifier: &mut Unifier) -> Result<TypeVarId, HashSet<String>> { pub fn get_var_id(var_ty: Type, unifier: &mut Unifier) -> Result<TypeVarId, HashSet<String>> {
if let TypeEnum::TVar { id, .. } = unifier.get_ty(var_ty).as_ref() { if let TypeEnum::TVar { id, .. } = unifier.get_ty(var_ty).as_ref() {
@ -933,139 +991,6 @@ impl TopLevelComposer {
)) ))
} }
} }
/// Parses the class type variables and direct parents
/// we only allow single inheritance
pub fn analyze_class_bases(
class_def: &Arc<RwLock<TopLevelDef>>,
class_ast: &Option<Stmt>,
temp_def_list: &[Arc<RwLock<TopLevelDef>>],
unifier: &mut Unifier,
primitives_store: &PrimitiveStore,
) -> Result<(), HashSet<String>> {
let mut class_def = class_def.write();
let (class_def_id, class_ancestors, class_bases_ast, class_type_vars, class_resolver) = {
let TopLevelDef::Class { object_id, ancestors, type_vars, resolver, .. } =
&mut *class_def
else {
unreachable!()
};
let Some(ast::Located { node: ast::StmtKind::ClassDef { bases, .. }, .. }) = class_ast
else {
unreachable!()
};
(object_id, ancestors, bases, type_vars, resolver.as_ref().unwrap().as_ref())
};
let mut is_generic = false;
let mut has_base = false;
// Check class bases for typevars
for b in class_bases_ast {
match &b.node {
// analyze typevars bounded to the class,
// only support things like `class A(Generic[T, V])`,
// things like `class A(Generic[T, V, ImportedModule.T])` is not supported
// i.e. only simple names are allowed in the subscript
// should update the TopLevelDef::Class.typevars and the TypeEnum::TObj.params
ast::ExprKind::Subscript { value, slice, .. } if matches!(&value.node, ast::ExprKind::Name { id, .. } if id == &"Generic".into()) =>
{
if is_generic {
return Err(HashSet::from([format!(
"only single Generic[...] is allowed (at {})",
b.location
)]));
}
is_generic = true;
let type_var_list: Vec<&ast::Expr<()>>;
// if `class A(Generic[T, V, G])`
if let ast::ExprKind::Tuple { elts, .. } = &slice.node {
type_var_list = elts.iter().collect_vec();
// `class A(Generic[T])`
} else {
type_var_list = vec![&**slice];
}
let type_vars = type_var_list
.into_iter()
.map(|e| {
class_resolver.parse_type_annotation(
temp_def_list,
unifier,
primitives_store,
e,
)
})
.collect::<Result<Vec<_>, _>>()?;
class_type_vars.extend(type_vars);
}
ast::ExprKind::Name { .. } | ast::ExprKind::Subscript { .. } => {
if has_base {
return Err(HashSet::from([format!("a class definition can only have at most one base class declaration and one generic declaration (at {})", b.location )]));
}
has_base = true;
// the function parse_ast_to make sure that no type var occurred in
// bast_ty if it is a CustomClassKind
let base_ty = parse_ast_to_type_annotation_kinds(
class_resolver,
temp_def_list,
unifier,
primitives_store,
b,
vec![(*class_def_id, class_type_vars.clone())]
.into_iter()
.collect::<HashMap<_, _>>(),
)?;
if let TypeAnnotation::CustomClass { .. } = &base_ty {
class_ancestors.push(base_ty);
} else {
return Err(HashSet::from([format!(
"class base declaration can only be custom class (at {})",
b.location
)]));
}
}
_ => {
return Err(HashSet::from([format!(
"unsupported statement in class defintion (at {})",
b.location
)]));
}
}
}
Ok(())
}
/// gets all ancestors of a class
pub fn analyze_class_ancestors(
class_def: &Arc<RwLock<TopLevelDef>>,
temp_def_list: &[Arc<RwLock<TopLevelDef>>],
) {
// Check if class has a direct parent
let mut class_def = class_def.write();
let TopLevelDef::Class { ancestors, type_vars, object_id, .. } = &mut *class_def else {
unreachable!()
};
let mut anc_set = HashMap::new();
if let Some(ancestor) = ancestors.first() {
let TypeAnnotation::CustomClass { id, .. } = ancestor else { unreachable!() };
let TopLevelDef::Class { ancestors: parent_ancestors, .. } =
&*temp_def_list[id.0].read()
else {
unreachable!()
};
for anc in parent_ancestors.iter().skip(1) {
let TypeAnnotation::CustomClass { id, .. } = anc else { unreachable!() };
anc_set.insert(id, anc.clone());
}
ancestors.extend(anc_set.into_values());
}
// push `self` as first ancestor of class
ancestors.insert(0, make_self_type_annotation(type_vars.as_slice(), *object_id));
}
} }
pub fn parse_parameter_default_value( pub fn parse_parameter_default_value(

View File

@ -3,10 +3,10 @@ source: nac3core/src/toplevel/test.rs
expression: res_vec expression: res_vec
--- ---
[ [
"Class {\nname: \"Generic_A\",\nancestors: [\"Generic_A[V]\", \"B\"],\nfields: [\"aa\", \"a\"],\nmethods: [(\"__init__\", \"fn[[], none]\"), (\"foo\", \"fn[[b:T], none]\"), (\"fun\", \"fn[[a:int32], V]\")],\ntype_vars: [\"V\"]\n}\n",
"Function {\nname: \"Generic_A.__init__\",\nsig: \"fn[[], none]\",\nvar_id: []\n}\n",
"Function {\nname: \"Generic_A.fun\",\nsig: \"fn[[a:int32], V]\",\nvar_id: [TypeVarId(241)]\n}\n",
"Class {\nname: \"B\",\nancestors: [\"B\"],\nfields: [\"aa\"],\nmethods: [(\"__init__\", \"fn[[], none]\"), (\"foo\", \"fn[[b:T], none]\")],\ntype_vars: []\n}\n", "Class {\nname: \"B\",\nancestors: [\"B\"],\nfields: [\"aa\"],\nmethods: [(\"__init__\", \"fn[[], none]\"), (\"foo\", \"fn[[b:T], none]\")],\ntype_vars: []\n}\n",
"Function {\nname: \"B.__init__\",\nsig: \"fn[[], none]\",\nvar_id: []\n}\n", "Function {\nname: \"B.__init__\",\nsig: \"fn[[], none]\",\nvar_id: []\n}\n",
"Function {\nname: \"B.foo\",\nsig: \"fn[[b:T], none]\",\nvar_id: []\n}\n", "Function {\nname: \"B.foo\",\nsig: \"fn[[b:T], none]\",\nvar_id: []\n}\n",
"Class {\nname: \"Generic_A\",\nancestors: [\"Generic_A[V]\", \"B\"],\nfields: [\"aa\", \"a\"],\nmethods: [(\"__init__\", \"fn[[], none]\"), (\"foo\", \"fn[[b:T], none]\"), (\"fun\", \"fn[[a:int32], V]\")],\ntype_vars: [\"V\"]\n}\n",
"Function {\nname: \"Generic_A.__init__\",\nsig: \"fn[[], none]\",\nvar_id: []\n}\n",
"Function {\nname: \"Generic_A.fun\",\nsig: \"fn[[a:int32], V]\",\nvar_id: [TypeVarId(246)]\n}\n",
] ]

View File

@ -7,11 +7,11 @@ expression: res_vec
"Function {\nname: \"A.__init__\",\nsig: \"fn[[], none]\",\nvar_id: []\n}\n", "Function {\nname: \"A.__init__\",\nsig: \"fn[[], none]\",\nvar_id: []\n}\n",
"Function {\nname: \"A.fun\",\nsig: \"fn[[b:B], none]\",\nvar_id: []\n}\n", "Function {\nname: \"A.fun\",\nsig: \"fn[[b:B], none]\",\nvar_id: []\n}\n",
"Function {\nname: \"A.foo\",\nsig: \"fn[[a:T, b:V], none]\",\nvar_id: [TypeVarId(249)]\n}\n", "Function {\nname: \"A.foo\",\nsig: \"fn[[a:T, b:V], none]\",\nvar_id: [TypeVarId(249)]\n}\n",
"Class {\nname: \"B\",\nancestors: [\"B\", \"C\", \"A\"],\nfields: [\"a\"],\nmethods: [(\"__init__\", \"fn[[], none]\"), (\"fun\", \"fn[[b:B], none]\"), (\"foo\", \"fn[[a:T, b:V], none]\")],\ntype_vars: []\n}\n",
"Function {\nname: \"B.__init__\",\nsig: \"fn[[], none]\",\nvar_id: []\n}\n",
"Class {\nname: \"C\",\nancestors: [\"C\", \"A\"],\nfields: [\"a\"],\nmethods: [(\"__init__\", \"fn[[], none]\"), (\"fun\", \"fn[[b:B], none]\"), (\"foo\", \"fn[[a:T, b:V], none]\")],\ntype_vars: []\n}\n", "Class {\nname: \"C\",\nancestors: [\"C\", \"A\"],\nfields: [\"a\"],\nmethods: [(\"__init__\", \"fn[[], none]\"), (\"fun\", \"fn[[b:B], none]\"), (\"foo\", \"fn[[a:T, b:V], none]\")],\ntype_vars: []\n}\n",
"Function {\nname: \"C.__init__\",\nsig: \"fn[[], none]\",\nvar_id: []\n}\n", "Function {\nname: \"C.__init__\",\nsig: \"fn[[], none]\",\nvar_id: []\n}\n",
"Function {\nname: \"C.fun\",\nsig: \"fn[[b:B], none]\",\nvar_id: []\n}\n", "Function {\nname: \"C.fun\",\nsig: \"fn[[b:B], none]\",\nvar_id: []\n}\n",
"Class {\nname: \"B\",\nancestors: [\"B\", \"C\", \"A\"],\nfields: [\"a\"],\nmethods: [(\"__init__\", \"fn[[], none]\"), (\"fun\", \"fn[[b:B], none]\"), (\"foo\", \"fn[[a:T, b:V], none]\")],\ntype_vars: []\n}\n",
"Function {\nname: \"B.__init__\",\nsig: \"fn[[], none]\",\nvar_id: []\n}\n",
"Function {\nname: \"foo\",\nsig: \"fn[[a:A], none]\",\nvar_id: []\n}\n", "Function {\nname: \"foo\",\nsig: \"fn[[a:A], none]\",\nvar_id: []\n}\n",
"Function {\nname: \"ff\",\nsig: \"fn[[a:T], V]\",\nvar_id: [TypeVarId(257)]\n}\n", "Function {\nname: \"ff\",\nsig: \"fn[[a:T], V]\",\nvar_id: [TypeVarId(257)]\n}\n",
] ]

View File

@ -229,6 +229,11 @@ fn test_simple_function_analyze(source: &[&str], tys: &[&str], names: &[&str]) {
def foo(self, a: T, b: V): def foo(self, a: T, b: V):
pass pass
"}, "},
indoc! {"
class B(C):
def __init__(self):
pass
"},
indoc! {" indoc! {"
class C(A): class C(A):
def __init__(self): def __init__(self):
@ -237,11 +242,6 @@ fn test_simple_function_analyze(source: &[&str], tys: &[&str], names: &[&str]) {
a = 1 a = 1
pass pass
"}, "},
indoc! {"
class B(C):
def __init__(self):
pass
"},
indoc! {" indoc! {"
def foo(a: A): def foo(a: A):
pass pass
@ -256,14 +256,6 @@ fn test_simple_function_analyze(source: &[&str], tys: &[&str], names: &[&str]) {
)] )]
#[test_case( #[test_case(
&[ &[
indoc! {"
class B:
aa: bool
def __init__(self):
self.aa = False
def foo(self, b: T):
pass
"},
indoc! {" indoc! {"
class Generic_A(Generic[V], B): class Generic_A(Generic[V], B):
a: int64 a: int64
@ -271,6 +263,14 @@ fn test_simple_function_analyze(source: &[&str], tys: &[&str], names: &[&str]) {
self.a = 123123123123 self.a = 123123123123
def fun(self, a: int32) -> V: def fun(self, a: int32) -> V:
pass pass
"},
indoc! {"
class B:
aa: bool
def __init__(self):
self.aa = False
def foo(self, b: T):
pass
"} "}
], ],
&[]; &[];
@ -390,18 +390,18 @@ fn test_simple_function_analyze(source: &[&str], tys: &[&str], names: &[&str]) {
pass pass
"} "}
], ],
&["NameError: name 'B' is not defined (at unknown:1:9)"]; &["cyclic inheritance detected"];
"cyclic1" "cyclic1"
)] )]
#[test_case( #[test_case(
&[ &[
indoc! {" indoc! {"
class B(Generic[V, T], C[int32]): class A(B[bool, int64]):
def __init__(self): def __init__(self):
pass pass
"}, "},
indoc! {" indoc! {"
class A(B[bool, int64]): class B(Generic[V, T], C[int32]):
def __init__(self): def __init__(self):
pass pass
"}, "},
@ -411,7 +411,7 @@ fn test_simple_function_analyze(source: &[&str], tys: &[&str], names: &[&str]) {
pass pass
"}, "},
], ],
&["NameError: name 'C' is not defined (at unknown:1:25)"]; &["cyclic inheritance detected"];
"cyclic2" "cyclic2"
)] )]
#[test_case( #[test_case(
@ -435,6 +435,11 @@ fn test_simple_function_analyze(source: &[&str], tys: &[&str], names: &[&str]) {
)] )]
#[test_case( #[test_case(
&[ &[
indoc! {"
class A(B, Generic[T], C):
def __init__(self):
pass
"},
indoc! {" indoc! {"
class B: class B:
def __init__(self): def __init__(self):
@ -444,11 +449,6 @@ fn test_simple_function_analyze(source: &[&str], tys: &[&str], names: &[&str]) {
class C: class C:
def __init__(self): def __init__(self):
pass pass
"},
indoc! {"
class A(B, Generic[T], C):
def __init__(self):
pass
"} "}
], ],

View File

@ -100,13 +100,7 @@ pub fn parse_ast_to_type_annotation_kinds<T, S: std::hash::BuildHasher + Clone>(
Ok(TypeAnnotation::CustomClass { id: PrimDef::Exception.id(), params: Vec::default() }) Ok(TypeAnnotation::CustomClass { id: PrimDef::Exception.id(), params: Vec::default() })
} else if let Ok(obj_id) = resolver.get_identifier_def(*id) { } else if let Ok(obj_id) = resolver.get_identifier_def(*id) {
let type_vars = { let type_vars = {
let Some(top_level_def) = top_level_defs.get(obj_id.0) else { let def_read = top_level_defs[obj_id.0].try_read();
return Err(HashSet::from([format!(
"NameError: name '{id}' is not defined (at {})",
expr.location
)]));
};
let def_read = top_level_def.try_read();
if let Some(def_read) = def_read { if let Some(def_read) = def_read {
if let TopLevelDef::Class { type_vars, .. } = &*def_read { if let TopLevelDef::Class { type_vars, .. } = &*def_read {
type_vars.clone() type_vars.clone()
@ -161,17 +155,12 @@ pub fn parse_ast_to_type_annotation_kinds<T, S: std::hash::BuildHasher + Clone>(
} }
let obj_id = resolver.get_identifier_def(*id)?; let obj_id = resolver.get_identifier_def(*id)?;
let type_vars = { let type_vars = {
let Some(top_level_def) = top_level_defs.get(obj_id.0) else { let def_read = top_level_defs[obj_id.0].try_read();
return Err(HashSet::from([format!(
"NameError: name '{id}' is not defined (at {})",
expr.location
)]));
};
let def_read = top_level_def.try_read();
if let Some(def_read) = def_read { if let Some(def_read) = def_read {
let TopLevelDef::Class { type_vars, .. } = &*def_read else { let TopLevelDef::Class { type_vars, .. } = &*def_read else {
unreachable!("must be class here") unreachable!("must be class here")
}; };
type_vars.clone() type_vars.clone()
} else { } else {
locked.get(&obj_id).unwrap().clone() locked.get(&obj_id).unwrap().clone()