Adding static class attribute definition functionality

master
aadityavardhan 5 months ago
parent 813bfa92a7
commit 92cc3d799f
  1. 3
      nac3core/src/toplevel/builtins.rs
  2. 49
      nac3core/src/toplevel/composer.rs
  3. 1
      nac3core/src/toplevel/helper.rs
  4. 2
      nac3core/src/toplevel/mod.rs

@ -74,6 +74,7 @@ pub fn get_exn_constructor(
constructor: Some(signature),
resolver: None,
loc: None,
static_fields: Default::default(),
};
(fun_def, class_def, signature, exn_type)
}
@ -175,6 +176,7 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
type_vars: Default::default(),
fields: exception_fields,
methods: Default::default(),
static_fields: Default::default(),
ancestors: vec![],
constructor: None,
resolver: None,
@ -200,6 +202,7 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
object_id: DefinitionId(10),
type_vars: vec![option_ty_var],
fields: vec![],
static_fields: vec![],
methods: vec![
("is_some".into(), is_some_ty.0, DefinitionId(11)),
("is_none".into(), is_some_ty.0, DefinitionId(12)),

@ -1040,6 +1040,7 @@ impl TopLevelComposer {
class_body_ast,
_class_ancestor_def,
class_fields_def,
class_static_fields_def, // Introduce static class attribute list into the function
class_methods_def,
class_type_vars_def,
class_resolver,
@ -1047,6 +1048,7 @@ impl TopLevelComposer {
object_id,
ancestors,
fields,
static_fields,
methods,
resolver,
type_vars,
@ -1054,7 +1056,7 @@ impl TopLevelComposer {
} = &mut *class_def
{
if let ast::StmtKind::ClassDef { name, bases, body, .. } = &class_ast {
(*object_id, *name, bases, body, ancestors, fields, methods, type_vars, resolver)
(*object_id, *name, bases, body, ancestors, fields, static_fields, methods, type_vars, resolver)
} else {
unreachable!("here must be class def ast");
}
@ -1268,7 +1270,8 @@ impl TopLevelComposer {
.unify(method_dummy_ty, method_type)
.map_err(|e| e.to_display(unifier).to_string())?;
}
ast::StmtKind::AnnAssign { target, annotation, value: None, .. } => {
// Reset value from none since fields in the form "ATTR_0: int32 = 10" need to be initialised
ast::StmtKind::AnnAssign { target, annotation, value, .. } => {
if let ast::ExprKind::Name { id: attr, .. } = &target.node {
if defined_fields.insert(attr.to_string()) {
let dummy_field_type = unifier.get_dummy_var().0;
@ -1294,6 +1297,10 @@ impl TopLevelComposer {
_ if core_config.kernel_ann.is_none() => (annotation, true),
_ => continue, // ignore fields annotated otherwise
};
// If the value node is provided, then it must be a static class attribute
if let Option::Some(..) = &value{
class_static_fields_def.push((*attr, dummy_field_type, mutable));
}
class_fields_def.push((*attr, dummy_field_type, mutable));
let parsed_annotation = parse_ast_to_type_annotation_kinds(
@ -1335,7 +1342,34 @@ impl TopLevelComposer {
));
}
}
ast::StmtKind::Assign { .. } => {}, // we don't class attributes
// Add assign branch since fields in the form "ATTR_0 = 5" in the class body qualify as static class attributes
// However, type checking and expression folding needs to be performed in order to correctly
// Infer the type of target
ast::StmtKind::Assign { targets, value, .. } => {
for target in targets {
if let ast::ExprKind::Name { id: attr, .. } = &target.node {
if defined_fields.insert(attr.to_string()) {
let dummy_field_type = unifier.get_dummy_var().0;
class_static_fields_def.push((*attr, dummy_field_type, true));
class_fields_def.push((*attr, dummy_field_type, true));
} else {
return Err(format!(
"same class fields `{}` defined twice (at {})",
attr, target.location
));
}
} else {
return Err(format!(
"unsupported statement type in class definition body (at {})",
target.location
));
}
}
},
ast::StmtKind::Pass { .. } => {}
ast::StmtKind::Expr { value: _, .. } => {} // typically a docstring; ignoring all expressions matches CPython behavior
_ => {
@ -1516,6 +1550,7 @@ impl TopLevelComposer {
ancestors,
methods,
fields,
static_fields, // Introduce static fields for (un)initialization check
type_vars,
name: class_name,
object_id,
@ -1618,11 +1653,13 @@ impl TopLevelComposer {
unreachable!("must be init function here")
}
let all_inited = Self::get_all_assigned_field(body.as_slice())?;
for (f, _, _) in fields {
if !all_inited.contains(f) {
// If a field is uninitialized but also a static class attribute, don't
// throw an error due to uninitialization
for f in fields {
if !all_inited.contains(&f.0) && !static_fields.contains(&f) {
return Err(format!(
"fields `{}` of class `{}` not fully initialized in the initializer (at {})",
f,
&f.0,
class_name,
body[0].location,
));

@ -162,6 +162,7 @@ impl TopLevelComposer {
object_id: DefinitionId(index),
type_vars: Default::default(),
fields: Default::default(),
static_fields: Default::default(), // Initialize for constructor
methods: Default::default(),
ancestors: Default::default(),
constructor,

@ -92,6 +92,8 @@ pub enum TopLevelDef {
// name, type, is mutable
fields: Vec<(StrRef, Type, bool)>,
// class methods, pointing to the corresponding function definition.
static_fields: Vec<(StrRef, Type, bool)>,
// list of static data members
methods: Vec<(StrRef, Type, DefinitionId)>,
// ancestor classes, including itself.
ancestors: Vec<TypeAnnotation>,

Loading…
Cancel
Save