From 92cc3d799fae125ea651e86f61643687f7665ffc Mon Sep 17 00:00:00 2001 From: aadityavardhan Date: Sun, 21 Aug 2022 17:57:35 +0530 Subject: [PATCH 1/2] Adding static class attribute definition functionality --- nac3core/src/toplevel/builtins.rs | 3 ++ nac3core/src/toplevel/composer.rs | 49 +++++++++++++++++++++++++++---- nac3core/src/toplevel/helper.rs | 1 + nac3core/src/toplevel/mod.rs | 2 ++ 4 files changed, 49 insertions(+), 6 deletions(-) diff --git a/nac3core/src/toplevel/builtins.rs b/nac3core/src/toplevel/builtins.rs index 15fa2e0..de3cc23 100644 --- a/nac3core/src/toplevel/builtins.rs +++ b/nac3core/src/toplevel/builtins.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)), diff --git a/nac3core/src/toplevel/composer.rs b/nac3core/src/toplevel/composer.rs index 16f508a..9728636 100644 --- a/nac3core/src/toplevel/composer.rs +++ b/nac3core/src/toplevel/composer.rs @@ -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, )); diff --git a/nac3core/src/toplevel/helper.rs b/nac3core/src/toplevel/helper.rs index 73eebab..77a3ba9 100644 --- a/nac3core/src/toplevel/helper.rs +++ b/nac3core/src/toplevel/helper.rs @@ -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, diff --git a/nac3core/src/toplevel/mod.rs b/nac3core/src/toplevel/mod.rs index 643594d..16a7f0b 100644 --- a/nac3core/src/toplevel/mod.rs +++ b/nac3core/src/toplevel/mod.rs @@ -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, -- 2.42.0 From 5a643bffcaeab67308709aba529d9d725e1cdc9d Mon Sep 17 00:00:00 2001 From: aadityavardhan Date: Sun, 21 Aug 2022 22:09:14 +0530 Subject: [PATCH 2/2] Style and formatting improvements --- nac3core/src/toplevel/builtins.rs | 2 +- nac3core/src/toplevel/composer.rs | 19 ++++--------------- nac3core/src/toplevel/helper.rs | 2 +- nac3core/src/toplevel/mod.rs | 4 ++-- 4 files changed, 8 insertions(+), 19 deletions(-) diff --git a/nac3core/src/toplevel/builtins.rs b/nac3core/src/toplevel/builtins.rs index de3cc23..a5b55c1 100644 --- a/nac3core/src/toplevel/builtins.rs +++ b/nac3core/src/toplevel/builtins.rs @@ -66,6 +66,7 @@ pub fn get_exn_constructor( object_id: DefinitionId(class_id), type_vars: Default::default(), fields: exception_fields, + static_fields: Default::default(), methods: vec![("__init__".into(), signature, DefinitionId(cons_id))], ancestors: vec![ TypeAnnotation::CustomClass { id: DefinitionId(class_id), params: Default::default() }, @@ -74,7 +75,6 @@ pub fn get_exn_constructor( constructor: Some(signature), resolver: None, loc: None, - static_fields: Default::default(), }; (fun_def, class_def, signature, exn_type) } diff --git a/nac3core/src/toplevel/composer.rs b/nac3core/src/toplevel/composer.rs index 9728636..88bdc62 100644 --- a/nac3core/src/toplevel/composer.rs +++ b/nac3core/src/toplevel/composer.rs @@ -1040,7 +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_static_fields_def, class_methods_def, class_type_vars_def, class_resolver, @@ -1270,7 +1270,6 @@ impl TopLevelComposer { .unify(method_dummy_ty, method_type) .map_err(|e| e.to_display(unifier).to_string())?; } - // 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()) { @@ -1297,7 +1296,6 @@ 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)); } @@ -1331,7 +1329,7 @@ impl TopLevelComposer { type_var_to_concrete_def.insert(dummy_field_type, parsed_annotation); } else { return Err(format!( - "same class fields `{}` defined twice (at {})", + "same class field `{}` defined twice (at {})", attr, target.location )); } @@ -1342,23 +1340,16 @@ impl TopLevelComposer { )); } } - - // 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 {})", + "same class field `{}` defined twice (at {})", attr, target.location )); } @@ -1550,7 +1541,7 @@ impl TopLevelComposer { ancestors, methods, fields, - static_fields, // Introduce static fields for (un)initialization check + static_fields, type_vars, name: class_name, object_id, @@ -1653,8 +1644,6 @@ impl TopLevelComposer { unreachable!("must be init function here") } let all_inited = Self::get_all_assigned_field(body.as_slice())?; - // 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!( diff --git a/nac3core/src/toplevel/helper.rs b/nac3core/src/toplevel/helper.rs index 77a3ba9..deba230 100644 --- a/nac3core/src/toplevel/helper.rs +++ b/nac3core/src/toplevel/helper.rs @@ -162,7 +162,7 @@ impl TopLevelComposer { object_id: DefinitionId(index), type_vars: Default::default(), fields: Default::default(), - static_fields: Default::default(), // Initialize for constructor + static_fields: Default::default(), methods: Default::default(), ancestors: Default::default(), constructor, diff --git a/nac3core/src/toplevel/mod.rs b/nac3core/src/toplevel/mod.rs index 16a7f0b..a93e9d0 100644 --- a/nac3core/src/toplevel/mod.rs +++ b/nac3core/src/toplevel/mod.rs @@ -91,9 +91,9 @@ pub enum TopLevelDef { // class fields // 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 + static_fields: Vec<(StrRef, Type, bool)>, + // class methods, pointing to the corresponding function definition. methods: Vec<(StrRef, Type, DefinitionId)>, // ancestor classes, including itself. ancestors: Vec, -- 2.42.0