nac3core: parse type annotation python forwardref handling

This commit is contained in:
ychenfo 2021-11-11 02:46:41 +08:00
parent 66a9eda3c1
commit dab06bdb58
3 changed files with 272 additions and 261 deletions

View File

@ -25,7 +25,6 @@ class virtual(Generic[T]):
import device_db import device_db
core_arguments = device_db.device_db["core"]["arguments"] core_arguments = device_db.device_db["core"]["arguments"]
compiler = nac3artiq.NAC3(core_arguments["target"]) compiler = nac3artiq.NAC3(core_arguments["target"])
allow_registration = True allow_registration = True
# Delay NAC3 analysis until all referenced variables are supposed to exist on the CPython side. # Delay NAC3 analysis until all referenced variables are supposed to exist on the CPython side.

View File

@ -13,7 +13,7 @@ use crate::{
use crate::{location::Location, typecheck::typedef::TypeEnum}; use crate::{location::Location, typecheck::typedef::TypeEnum};
use inkwell::values::BasicValueEnum; use inkwell::values::BasicValueEnum;
use itertools::{chain, izip}; use itertools::{chain, izip};
use nac3parser::ast::{Expr, StrRef}; use nac3parser::ast::{Constant::Str, Expr, StrRef};
use parking_lot::RwLock; use parking_lot::RwLock;
#[derive(Clone, PartialEq, Debug)] #[derive(Clone, PartialEq, Debug)]
@ -79,159 +79,168 @@ pub fn parse_type_annotation<T>(
let list_id = ids[6]; let list_id = ids[6];
let tuple_id = ids[7]; let tuple_id = ids[7];
match &expr.node { let name_handling = |id: &StrRef, unifier: &mut Unifier| {
Name { id, .. } => { if *id == int32_id {
if *id == int32_id { Ok(primitives.int32)
Ok(primitives.int32) } else if *id == int64_id {
} else if *id == int64_id { Ok(primitives.int64)
Ok(primitives.int64) } else if *id == float_id {
} else if *id == float_id { Ok(primitives.float)
Ok(primitives.float) } else if *id == bool_id {
} else if *id == bool_id { Ok(primitives.bool)
Ok(primitives.bool) } else if *id == none_id {
} else if *id == none_id { Ok(primitives.none)
Ok(primitives.none) } else {
} else { let obj_id = resolver.get_identifier_def(*id);
let obj_id = resolver.get_identifier_def(*id); if let Some(obj_id) = obj_id {
if let Some(obj_id) = obj_id { let def = top_level_defs[obj_id.0].read();
let def = top_level_defs[obj_id.0].read(); if let TopLevelDef::Class { fields, methods, type_vars, .. } = &*def {
if let TopLevelDef::Class { fields, methods, type_vars, .. } = &*def { if !type_vars.is_empty() {
if !type_vars.is_empty() { return Err(format!(
return Err(format!( "Unexpected number of type parameters: expected {} but got 0",
"Unexpected number of type parameters: expected {} but got 0", type_vars.len()
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 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 { } else {
// it could be a type variable Err("Cannot use function name as type".into())
let ty = resolver }
.get_symbol_type(unifier, top_level_defs, primitives, *id) } else {
.ok_or_else(|| "unknown type variable name".to_owned())?; // it could be a type variable
if let TypeEnum::TVar { .. } = &*unifier.get_ty(ty) { let ty = resolver
Ok(ty) .get_symbol_type(unifier, top_level_defs, primitives, *id)
} else { .ok_or_else(|| "unknown type variable name".to_owned())?;
Err(format!("Unknown type annotation {}", id)) 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 subscript_name_handle = |id: &StrRef, slice: &Expr<T>, unifier: &mut Unifier| {
let ty = parse_type_annotation( if *id == virtual_id {
resolver, let ty = parse_type_annotation(
top_level_defs, resolver,
unifier, top_level_defs,
primitives, unifier,
slice, primitives,
)?; slice,
Ok(unifier.add_ty(TypeEnum::TVirtual { ty })) )?;
} else if *id == list_id { Ok(unifier.add_ty(TypeEnum::TVirtual { ty }))
let ty = parse_type_annotation( } else if *id == list_id {
resolver, let ty = parse_type_annotation(
top_level_defs, resolver,
unifier, top_level_defs,
primitives, unifier,
slice, primitives,
)?; slice,
Ok(unifier.add_ty(TypeEnum::TList { ty })) )?;
} else if *id == tuple_id { Ok(unifier.add_ty(TypeEnum::TList { ty }))
if let Tuple { elts, .. } = &slice.node { } else if *id == tuple_id {
let ty = elts if let Tuple { elts, .. } = &slice.node {
.iter() let ty = elts
.map(|elt| { .iter()
parse_type_annotation( .map(|elt| {
resolver, parse_type_annotation(
top_level_defs,
unifier,
primitives,
elt,
)
})
.collect::<Result<Vec<_>, _>>()?;
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::<Result<Vec<_>, _>>()?
} else {
vec![parse_type_annotation(
resolver, resolver,
top_level_defs, top_level_defs,
unifier, unifier,
primitives, primitives,
slice, elt,
)?] )
}; })
.collect::<Result<Vec<_>, _>>()?;
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::<Result<Vec<_>, _>>()?
} else {
vec![parse_type_annotation(
resolver,
top_level_defs,
unifier,
primitives,
slice,
)?]
};
let obj_id = resolver let obj_id = resolver
.get_identifier_def(*id) .get_identifier_def(*id)
.ok_or_else(|| format!("Unknown type annotation {}", id))?; .ok_or_else(|| format!("Unknown type annotation {}", id))?;
let def = top_level_defs[obj_id.0].read(); let def = top_level_defs[obj_id.0].read();
if let TopLevelDef::Class { fields, methods, type_vars, .. } = &*def { if let TopLevelDef::Class { fields, methods, type_vars, .. } = &*def {
if types.len() != type_vars.len() { if types.len() != type_vars.len() {
return Err(format!( return Err(format!(
"Unexpected number of type parameters: expected {} but got {}", "Unexpected number of type parameters: expected {} but got {}",
type_vars.len(), type_vars.len(),
types.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::<HashMap<_, _>>();
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 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::<HashMap<_, _>>();
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 { } else {
Err("unsupported type expression".into()) Err("unsupported type expression".into())
} }

View File

@ -1,7 +1,7 @@
use std::cell::RefCell; use std::cell::RefCell;
use crate::typecheck::typedef::TypeVarMeta; use crate::typecheck::typedef::TypeVarMeta;
use ast::Constant::Str;
use super::*; use super::*;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
@ -49,58 +49,127 @@ pub fn parse_ast_to_type_annotation_kinds<T>(
primitives: &PrimitiveStore, primitives: &PrimitiveStore,
expr: &ast::Expr<T>, expr: &ast::Expr<T>,
// the key stores the type_var of this topleveldef::class, we only need this field here // the key stores the type_var of this topleveldef::class, we only need this field here
mut locked: HashMap<DefinitionId, Vec<Type>>, locked: HashMap<DefinitionId, Vec<Type>>,
) -> Result<TypeAnnotation, String> { ) -> Result<TypeAnnotation, String> {
match &expr.node { let name_handle = |id: &StrRef, unifier: &mut Unifier, locked: HashMap<DefinitionId, Vec<Type>>| {
ast::ExprKind::Name { id, .. } => { if id == &"int32".into() {
if id == &"int32".into() { Ok(TypeAnnotation::Primitive(primitives.int32))
Ok(TypeAnnotation::Primitive(primitives.int32)) } else if id == &"int64".into() {
} else if id == &"int64".into() { Ok(TypeAnnotation::Primitive(primitives.int64))
Ok(TypeAnnotation::Primitive(primitives.int64)) } else if id == &"float".into() {
} else if id == &"float".into() { Ok(TypeAnnotation::Primitive(primitives.float))
Ok(TypeAnnotation::Primitive(primitives.float)) } else if id == &"bool".into() {
} else if id == &"bool".into() { Ok(TypeAnnotation::Primitive(primitives.bool))
Ok(TypeAnnotation::Primitive(primitives.bool)) } else if id == &"None".into() {
} else if id == &"None".into() { Ok(TypeAnnotation::Primitive(primitives.none))
Ok(TypeAnnotation::Primitive(primitives.none)) } else if id == &"str".into() {
} else if id == &"str".into() { Ok(TypeAnnotation::Primitive(primitives.str))
Ok(TypeAnnotation::Primitive(primitives.str)) } else if let Some(obj_id) = resolver.get_identifier_def(*id) {
} else if let Some(obj_id) = resolver.get_identifier_def(*id) { let type_vars = {
let type_vars = { let def_read = top_level_defs[obj_id.0].try_read();
let def_read = top_level_defs[obj_id.0].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()
} else {
return Err("function cannot be used as a type".into());
}
} else { } 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 { } 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<T>, unifier: &mut Unifier, mut locked: HashMap<DefinitionId, Vec<Type>>| {
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 { } 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::<Result<Vec<_>, _>>()?;
// 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 // virtual
ast::ExprKind::Subscript { value, slice, .. } ast::ExprKind::Subscript { value, slice, .. }
if { 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( let def = parse_ast_to_type_annotation_kinds(
@ -120,7 +189,8 @@ pub fn parse_ast_to_type_annotation_kinds<T>(
// list // list
ast::ExprKind::Subscript { value, slice, .. } ast::ExprKind::Subscript { value, slice, .. }
if { 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( let def_ann = parse_ast_to_type_annotation_kinds(
@ -137,7 +207,8 @@ pub fn parse_ast_to_type_annotation_kinds<T>(
// tuple // tuple
ast::ExprKind::Subscript { value, slice, .. } ast::ExprKind::Subscript { value, slice, .. }
if { 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 { if let ast::ExprKind::Tuple { elts, .. } = &slice.node {
@ -163,71 +234,9 @@ pub fn parse_ast_to_type_annotation_kinds<T>(
// custom class // custom class
ast::ExprKind::Subscript { value, slice, .. } => { ast::ExprKind::Subscript { value, slice, .. } => {
if let ast::ExprKind::Name { id, .. } = &value.node { if let ast::ExprKind::Name { id, .. } = &value.node {
if vec!["virtual".into(), "Generic".into(), "list".into(), "tuple".into()] class_name_handle(id, slice, unifier, locked)
.contains(id) } else if let ast::ExprKind::Constant { value: Str(id), .. } = &value.node {
{ class_name_handle(&id.clone().into(), slice, unifier, locked)
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::<Result<Vec<_>, _>>()?;
// 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 })
} else { } else {
Err("unsupported expression type for class name".into()) 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 /// 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 /// 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 /// 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 \ /// 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`
pub fn make_self_type_annotation(type_vars: &[Type], object_id: DefinitionId) -> TypeAnnotation { pub fn make_self_type_annotation(type_vars: &[Type], object_id: DefinitionId) -> TypeAnnotation {
TypeAnnotation::CustomClass { TypeAnnotation::CustomClass {
id: object_id, id: object_id,