From dab06bdb58c102a808466a7e655253c17b791d93 Mon Sep 17 00:00:00 2001 From: ychenfo Date: Thu, 11 Nov 2021 02:46:41 +0800 Subject: [PATCH] nac3core: parse type annotation python forwardref handling --- nac3artiq/demo/min_artiq.py | 1 - nac3core/src/symbol_resolver.rs | 295 ++++++++++++----------- nac3core/src/toplevel/type_annotation.rs | 237 +++++++++--------- 3 files changed, 272 insertions(+), 261 deletions(-) diff --git a/nac3artiq/demo/min_artiq.py b/nac3artiq/demo/min_artiq.py index f644eee..fcb59ce 100644 --- a/nac3artiq/demo/min_artiq.py +++ b/nac3artiq/demo/min_artiq.py @@ -25,7 +25,6 @@ class virtual(Generic[T]): import device_db core_arguments = device_db.device_db["core"]["arguments"] - compiler = nac3artiq.NAC3(core_arguments["target"]) allow_registration = True # Delay NAC3 analysis until all referenced variables are supposed to exist on the CPython side. diff --git a/nac3core/src/symbol_resolver.rs b/nac3core/src/symbol_resolver.rs index 161d78a..9f9c161 100644 --- a/nac3core/src/symbol_resolver.rs +++ b/nac3core/src/symbol_resolver.rs @@ -13,7 +13,7 @@ use crate::{ use crate::{location::Location, typecheck::typedef::TypeEnum}; use inkwell::values::BasicValueEnum; use itertools::{chain, izip}; -use nac3parser::ast::{Expr, StrRef}; +use nac3parser::ast::{Constant::Str, Expr, StrRef}; use parking_lot::RwLock; #[derive(Clone, PartialEq, Debug)] @@ -79,159 +79,168 @@ pub fn parse_type_annotation( let list_id = ids[6]; let tuple_id = ids[7]; - match &expr.node { - Name { id, .. } => { - if *id == int32_id { - Ok(primitives.int32) - } else if *id == int64_id { - Ok(primitives.int64) - } else if *id == float_id { - Ok(primitives.float) - } else if *id == bool_id { - Ok(primitives.bool) - } else if *id == none_id { - Ok(primitives.none) - } else { - let obj_id = resolver.get_identifier_def(*id); - if let Some(obj_id) = obj_id { - let def = top_level_defs[obj_id.0].read(); - if let TopLevelDef::Class { fields, methods, type_vars, .. } = &*def { - if !type_vars.is_empty() { - return Err(format!( - "Unexpected number of type parameters: expected {} but got 0", - type_vars.len() - )); - } - let fields = RefCell::new( - chain( - fields.iter().map(|(k, v, m)| (*k, (*v, *m))), - methods.iter().map(|(k, v, _)| (*k, (*v, false))), - ) - .collect(), - ); - Ok(unifier.add_ty(TypeEnum::TObj { - obj_id, - fields, - params: Default::default(), - })) - } else { - Err("Cannot use function name as type".into()) + let name_handling = |id: &StrRef, unifier: &mut Unifier| { + if *id == int32_id { + Ok(primitives.int32) + } else if *id == int64_id { + Ok(primitives.int64) + } else if *id == float_id { + Ok(primitives.float) + } else if *id == bool_id { + Ok(primitives.bool) + } else if *id == none_id { + Ok(primitives.none) + } else { + let obj_id = resolver.get_identifier_def(*id); + if let Some(obj_id) = obj_id { + let def = top_level_defs[obj_id.0].read(); + if let TopLevelDef::Class { fields, methods, type_vars, .. } = &*def { + if !type_vars.is_empty() { + return Err(format!( + "Unexpected number of type parameters: expected {} but got 0", + type_vars.len() + )); } + let fields = RefCell::new( + chain( + fields.iter().map(|(k, v, m)| (*k, (*v, *m))), + methods.iter().map(|(k, v, _)| (*k, (*v, false))), + ) + .collect(), + ); + Ok(unifier.add_ty(TypeEnum::TObj { + obj_id, + fields, + params: Default::default(), + })) } else { - // it could be a type variable - let ty = resolver - .get_symbol_type(unifier, top_level_defs, primitives, *id) - .ok_or_else(|| "unknown type variable name".to_owned())?; - if let TypeEnum::TVar { .. } = &*unifier.get_ty(ty) { - Ok(ty) - } else { - Err(format!("Unknown type annotation {}", id)) - } + Err("Cannot use function name as type".into()) + } + } else { + // it could be a type variable + let ty = resolver + .get_symbol_type(unifier, top_level_defs, primitives, *id) + .ok_or_else(|| "unknown type variable name".to_owned())?; + if let TypeEnum::TVar { .. } = &*unifier.get_ty(ty) { + Ok(ty) + } else { + Err(format!("Unknown type annotation {}", id)) } } } - Subscript { value, slice, .. } => { - if let Name { id, .. } = &value.node { - if *id == virtual_id { - let ty = parse_type_annotation( - resolver, - top_level_defs, - unifier, - primitives, - slice, - )?; - Ok(unifier.add_ty(TypeEnum::TVirtual { ty })) - } else if *id == list_id { - let ty = parse_type_annotation( - resolver, - top_level_defs, - unifier, - primitives, - slice, - )?; - Ok(unifier.add_ty(TypeEnum::TList { ty })) - } else if *id == tuple_id { - if let Tuple { elts, .. } = &slice.node { - let ty = elts - .iter() - .map(|elt| { - parse_type_annotation( - resolver, - top_level_defs, - unifier, - primitives, - elt, - ) - }) - .collect::, _>>()?; - Ok(unifier.add_ty(TypeEnum::TTuple { ty })) - } else { - Err("Expected multiple elements for tuple".into()) - } - } else { - let types = if let Tuple { elts, .. } = &slice.node { - elts.iter() - .map(|v| { - parse_type_annotation( - resolver, - top_level_defs, - unifier, - primitives, - v, - ) - }) - .collect::, _>>()? - } else { - vec![parse_type_annotation( + }; + + let subscript_name_handle = |id: &StrRef, slice: &Expr, unifier: &mut Unifier| { + if *id == virtual_id { + let ty = parse_type_annotation( + resolver, + top_level_defs, + unifier, + primitives, + slice, + )?; + Ok(unifier.add_ty(TypeEnum::TVirtual { ty })) + } else if *id == list_id { + let ty = parse_type_annotation( + resolver, + top_level_defs, + unifier, + primitives, + slice, + )?; + Ok(unifier.add_ty(TypeEnum::TList { ty })) + } else if *id == tuple_id { + if let Tuple { elts, .. } = &slice.node { + let ty = elts + .iter() + .map(|elt| { + parse_type_annotation( resolver, top_level_defs, unifier, primitives, - slice, - )?] - }; + elt, + ) + }) + .collect::, _>>()?; + Ok(unifier.add_ty(TypeEnum::TTuple { ty })) + } else { + Err("Expected multiple elements for tuple".into()) + } + } else { + let types = if let Tuple { elts, .. } = &slice.node { + elts.iter() + .map(|v| { + parse_type_annotation( + resolver, + top_level_defs, + unifier, + primitives, + v, + ) + }) + .collect::, _>>()? + } else { + vec![parse_type_annotation( + resolver, + top_level_defs, + unifier, + primitives, + slice, + )?] + }; - let obj_id = resolver - .get_identifier_def(*id) - .ok_or_else(|| format!("Unknown type annotation {}", id))?; - let def = top_level_defs[obj_id.0].read(); - if let TopLevelDef::Class { fields, methods, type_vars, .. } = &*def { - if types.len() != type_vars.len() { - return Err(format!( - "Unexpected number of type parameters: expected {} but got {}", - type_vars.len(), - types.len() - )); - } - let mut subst = HashMap::new(); - for (var, ty) in izip!(type_vars.iter(), types.iter()) { - let id = if let TypeEnum::TVar { id, .. } = &*unifier.get_ty(*var) { - *id - } else { - unreachable!() - }; - subst.insert(id, *ty); - } - let mut fields = fields - .iter() - .map(|(attr, ty, is_mutable)| { - let ty = unifier.subst(*ty, &subst).unwrap_or(*ty); - (*attr, (ty, *is_mutable)) - }) - .collect::>(); - fields.extend(methods.iter().map(|(attr, ty, _)| { - let ty = unifier.subst(*ty, &subst).unwrap_or(*ty); - (*attr, (ty, false)) - })); - Ok(unifier.add_ty(TypeEnum::TObj { - obj_id, - fields: fields.into(), - params: subst.into(), - })) - } else { - Err("Cannot use function name as type".into()) - } + let obj_id = resolver + .get_identifier_def(*id) + .ok_or_else(|| format!("Unknown type annotation {}", id))?; + let def = top_level_defs[obj_id.0].read(); + if let TopLevelDef::Class { fields, methods, type_vars, .. } = &*def { + if types.len() != type_vars.len() { + return Err(format!( + "Unexpected number of type parameters: expected {} but got {}", + type_vars.len(), + types.len() + )); } + let mut subst = HashMap::new(); + for (var, ty) in izip!(type_vars.iter(), types.iter()) { + let id = if let TypeEnum::TVar { id, .. } = &*unifier.get_ty(*var) { + *id + } else { + unreachable!() + }; + subst.insert(id, *ty); + } + let mut fields = fields + .iter() + .map(|(attr, ty, is_mutable)| { + let ty = unifier.subst(*ty, &subst).unwrap_or(*ty); + (*attr, (ty, *is_mutable)) + }) + .collect::>(); + fields.extend(methods.iter().map(|(attr, ty, _)| { + let ty = unifier.subst(*ty, &subst).unwrap_or(*ty); + (*attr, (ty, false)) + })); + Ok(unifier.add_ty(TypeEnum::TObj { + obj_id, + fields: fields.into(), + params: subst.into(), + })) + } else { + Err("Cannot use function name as type".into()) + } + } + }; + + match &expr.node { + Name { id, .. } => name_handling(id, unifier), + Constant { value: Str(id), .. } => name_handling(&id.clone().into(), unifier), + Subscript { value, slice, .. } => { + if let Name { id, .. } = &value.node { + subscript_name_handle(id, slice, unifier) + } else if let Constant { value: Str(id), .. } = &value.node { + subscript_name_handle(&id.clone().into(), slice, unifier) } else { Err("unsupported type expression".into()) } diff --git a/nac3core/src/toplevel/type_annotation.rs b/nac3core/src/toplevel/type_annotation.rs index 257d582..7d6523a 100644 --- a/nac3core/src/toplevel/type_annotation.rs +++ b/nac3core/src/toplevel/type_annotation.rs @@ -1,7 +1,7 @@ use std::cell::RefCell; use crate::typecheck::typedef::TypeVarMeta; - +use ast::Constant::Str; use super::*; #[derive(Clone, Debug)] @@ -49,58 +49,127 @@ pub fn parse_ast_to_type_annotation_kinds( primitives: &PrimitiveStore, expr: &ast::Expr, // the key stores the type_var of this topleveldef::class, we only need this field here - mut locked: HashMap>, + locked: HashMap>, ) -> Result { - match &expr.node { - ast::ExprKind::Name { id, .. } => { - if id == &"int32".into() { - Ok(TypeAnnotation::Primitive(primitives.int32)) - } else if id == &"int64".into() { - Ok(TypeAnnotation::Primitive(primitives.int64)) - } else if id == &"float".into() { - Ok(TypeAnnotation::Primitive(primitives.float)) - } else if id == &"bool".into() { - Ok(TypeAnnotation::Primitive(primitives.bool)) - } else if id == &"None".into() { - Ok(TypeAnnotation::Primitive(primitives.none)) - } else if id == &"str".into() { - Ok(TypeAnnotation::Primitive(primitives.str)) - } else if let Some(obj_id) = resolver.get_identifier_def(*id) { - let type_vars = { - let def_read = top_level_defs[obj_id.0].try_read(); - if let Some(def_read) = def_read { - if let TopLevelDef::Class { type_vars, .. } = &*def_read { - type_vars.clone() - } else { - return Err("function cannot be used as a type".into()); - } + let name_handle = |id: &StrRef, unifier: &mut Unifier, locked: HashMap>| { + if id == &"int32".into() { + Ok(TypeAnnotation::Primitive(primitives.int32)) + } else if id == &"int64".into() { + Ok(TypeAnnotation::Primitive(primitives.int64)) + } else if id == &"float".into() { + Ok(TypeAnnotation::Primitive(primitives.float)) + } else if id == &"bool".into() { + Ok(TypeAnnotation::Primitive(primitives.bool)) + } else if id == &"None".into() { + Ok(TypeAnnotation::Primitive(primitives.none)) + } else if id == &"str".into() { + Ok(TypeAnnotation::Primitive(primitives.str)) + } else if let Some(obj_id) = resolver.get_identifier_def(*id) { + let type_vars = { + let def_read = top_level_defs[obj_id.0].try_read(); + if let Some(def_read) = def_read { + if let TopLevelDef::Class { type_vars, .. } = &*def_read { + type_vars.clone() } else { - locked.get(&obj_id).unwrap().clone() + return Err("function cannot be used as a type".into()); } - }; - // check param number here - if !type_vars.is_empty() { - return Err(format!( - "expect {} type variable parameter but got 0", - type_vars.len() - )); - } - Ok(TypeAnnotation::CustomClass { id: obj_id, params: vec![] }) - } else if let Some(ty) = resolver.get_symbol_type(unifier, top_level_defs, primitives, *id) { - if let TypeEnum::TVar { .. } = unifier.get_ty(ty).as_ref() { - Ok(TypeAnnotation::TypeVar(ty)) } else { - Err("not a type variable identifier".into()) + locked.get(&obj_id).unwrap().clone() + } + }; + // check param number here + if !type_vars.is_empty() { + return Err(format!( + "expect {} type variable parameter but got 0", + type_vars.len() + )); + } + Ok(TypeAnnotation::CustomClass { id: obj_id, params: vec![] }) + } else if let Some(ty) = resolver.get_symbol_type(unifier, top_level_defs, primitives, *id) { + if let TypeEnum::TVar { .. } = unifier.get_ty(ty).as_ref() { + Ok(TypeAnnotation::TypeVar(ty)) + } else { + Err("not a type variable identifier".into()) + } + } else { + Err("name cannot be parsed as a type annotation".into()) + } + }; + + let class_name_handle = + |id: &StrRef, slice: &ast::Expr, unifier: &mut Unifier, mut locked: HashMap>| { + if vec!["virtual".into(), "Generic".into(), "list".into(), "tuple".into()] + .contains(id) + { + return Err("keywords cannot be class name".into()); + } + let obj_id = resolver + .get_identifier_def(*id) + .ok_or_else(|| "unknown class name".to_string())?; + let type_vars = { + let def_read = top_level_defs[obj_id.0].try_read(); + if let Some(def_read) = def_read { + if let TopLevelDef::Class { type_vars, .. } = &*def_read { + type_vars.clone() + } else { + unreachable!("must be class here") } } else { - Err("name cannot be parsed as a type annotation".into()) + locked.get(&obj_id).unwrap().clone() } - } - + }; + // we do not check whether the application of type variables are compatible here + let param_type_infos = { + let params_ast = if let ast::ExprKind::Tuple { elts, .. } = &slice.node { + elts.iter().collect_vec() + } else { + vec![slice] + }; + if type_vars.len() != params_ast.len() { + return Err(format!( + "expect {} type parameters but got {}", + type_vars.len(), + params_ast.len() + )); + } + let result = params_ast + .into_iter() + .map(|x| { + parse_ast_to_type_annotation_kinds( + resolver, + top_level_defs, + unifier, + primitives, + x, + { + locked.insert(obj_id, type_vars.clone()); + locked.clone() + }, + ) + }) + .collect::, _>>()?; + // make sure the result do not contain any type vars + let no_type_var = result + .iter() + .all(|x| get_type_var_contained_in_type_annotation(x).is_empty()); + if no_type_var { + result + } else { + return Err("application of type vars to generic class \ + is not currently supported" + .into()); + } + }; + Ok(TypeAnnotation::CustomClass { id: obj_id, params: param_type_infos }) + }; + match &expr.node { + ast::ExprKind::Name { id, .. } => name_handle(id, unifier, locked), + ast::ExprKind::Constant { value: Str(id), .. } => name_handle(&id.clone().into(), unifier, locked), // virtual ast::ExprKind::Subscript { value, slice, .. } if { - matches!(&value.node, ast::ExprKind::Name { id, .. } if id == &"virtual".into()) + matches!(&value.node, ast::ExprKind::Name { id, .. } if id == &"virtual".into()) || + matches!(&value.node, ast::ExprKind::Constant { value: Str(id), .. } if id == "virtual") } => { let def = parse_ast_to_type_annotation_kinds( @@ -120,7 +189,8 @@ pub fn parse_ast_to_type_annotation_kinds( // list ast::ExprKind::Subscript { value, slice, .. } if { - matches!(&value.node, ast::ExprKind::Name { id, .. } if id == &"list".into()) + matches!(&value.node, ast::ExprKind::Name { id, .. } if id == &"list".into()) || + matches!(&value.node, ast::ExprKind::Constant { value: Str(id), .. } if id == "list") } => { let def_ann = parse_ast_to_type_annotation_kinds( @@ -137,7 +207,8 @@ pub fn parse_ast_to_type_annotation_kinds( // tuple ast::ExprKind::Subscript { value, slice, .. } if { - matches!(&value.node, ast::ExprKind::Name { id, .. } if id == &"tuple".into()) + matches!(&value.node, ast::ExprKind::Name { id, .. } if id == &"tuple".into()) || + matches!(&value.node, ast::ExprKind::Constant { value: Str(id), .. } if id == "tuple") } => { if let ast::ExprKind::Tuple { elts, .. } = &slice.node { @@ -163,71 +234,9 @@ pub fn parse_ast_to_type_annotation_kinds( // custom class ast::ExprKind::Subscript { value, slice, .. } => { if let ast::ExprKind::Name { id, .. } = &value.node { - if vec!["virtual".into(), "Generic".into(), "list".into(), "tuple".into()] - .contains(id) - { - return Err("keywords cannot be class name".into()); - } - let obj_id = resolver - .get_identifier_def(*id) - .ok_or_else(|| "unknown class name".to_string())?; - let type_vars = { - let def_read = top_level_defs[obj_id.0].try_read(); - if let Some(def_read) = def_read { - if let TopLevelDef::Class { type_vars, .. } = &*def_read { - type_vars.clone() - } else { - unreachable!("must be class here") - } - } else { - locked.get(&obj_id).unwrap().clone() - } - }; - // we do not check whether the application of type variables are compatible here - let param_type_infos = { - let params_ast = if let ast::ExprKind::Tuple { elts, .. } = &slice.node { - elts.iter().collect_vec() - } else { - vec![slice.as_ref()] - }; - if type_vars.len() != params_ast.len() { - return Err(format!( - "expect {} type parameters but got {}", - type_vars.len(), - params_ast.len() - )); - } - let result = params_ast - .into_iter() - .map(|x| { - parse_ast_to_type_annotation_kinds( - resolver, - top_level_defs, - unifier, - primitives, - x, - { - locked.insert(obj_id, type_vars.clone()); - locked.clone() - }, - ) - }) - .collect::, _>>()?; - - // make sure the result do not contain any type vars - let no_type_var = result - .iter() - .all(|x| get_type_var_contained_in_type_annotation(x).is_empty()); - if no_type_var { - result - } else { - return Err("application of type vars to generic class \ - is not currently supported" - .into()); - } - }; - - Ok(TypeAnnotation::CustomClass { id: obj_id, params: param_type_infos }) + class_name_handle(id, slice, unifier, locked) + } else if let ast::ExprKind::Constant { value: Str(id), .. } = &value.node { + class_name_handle(&id.clone().into(), slice, unifier, locked) } else { Err("unsupported expression type for class name".into()) } @@ -368,13 +377,7 @@ pub fn get_type_from_type_annotation_kinds( /// But note that here we do not make a duplication of `T`, `V`, we direclty /// use them as they are in the TopLevelDef::Class since those in the /// TopLevelDef::Class.type_vars will be substitute later when seeing applications/instantiations -/// the Type of their fields and methods will also be subst when application/instantiation \ -/// \ -/// Note this implicit self type is different with seeing `A[T, V]` explicitly outside -/// the class def ast body, where it is a new instantiation of the generic class `A`, -/// but equivalent to seeing `A[T, V]` inside the class def body ast, where although we -/// create copies of `T` and `V`, we will find them out as occured type vars in the analyze_class() -/// and unify them with the class generic `T`, `V` +/// the Type of their fields and methods will also be subst when application/instantiation pub fn make_self_type_annotation(type_vars: &[Type], object_id: DefinitionId) -> TypeAnnotation { TypeAnnotation::CustomClass { id: object_id,