forked from M-Labs/nac3
Compare commits
9 Commits
refactor_c
...
master
Author | SHA1 | Date | |
---|---|---|---|
5839badadd | |||
56c845aac4 | |||
65a12d9ab3 | |||
9c6685fa8f | |||
2bb788e4bb | |||
42a2f243b5 | |||
3ce2eddcdc | |||
51bf126a32 | |||
1a197c67f6 |
@ -2886,7 +2886,31 @@ pub fn gen_expr<'ctx, G: CodeGenerator>(
|
|||||||
Some((_, Some(static_value), _)) => ValueEnum::Static(static_value.clone()),
|
Some((_, Some(static_value), _)) => ValueEnum::Static(static_value.clone()),
|
||||||
None => {
|
None => {
|
||||||
let resolver = ctx.resolver.clone();
|
let resolver = ctx.resolver.clone();
|
||||||
resolver.get_symbol_value(*id, ctx, generator).unwrap()
|
let value = resolver.get_symbol_value(*id, ctx, generator).unwrap();
|
||||||
|
|
||||||
|
let globals = ctx
|
||||||
|
.top_level
|
||||||
|
.definitions
|
||||||
|
.read()
|
||||||
|
.iter()
|
||||||
|
.filter_map(|def| {
|
||||||
|
if let TopLevelDef::Variable { simple_name, ty, .. } = &*def.read() {
|
||||||
|
Some((*simple_name, *ty))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect_vec();
|
||||||
|
|
||||||
|
if let Some((_, ty)) = globals.iter().find(|(name, _)| name == id) {
|
||||||
|
let ptr = value
|
||||||
|
.to_basic_value_enum(ctx, generator, *ty)
|
||||||
|
.map(BasicValueEnum::into_pointer_value)?;
|
||||||
|
|
||||||
|
ctx.builder.build_load(ptr, id.to_string().as_str()).map(Into::into).unwrap()
|
||||||
|
} else {
|
||||||
|
value
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ExprKind::List { elts, .. } => {
|
ExprKind::List { elts, .. } => {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use nac3parser::ast::{fold::Fold, ExprKind};
|
use nac3parser::ast::{fold::Fold, ExprKind, Ident};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::{
|
use crate::{
|
||||||
@ -382,32 +382,66 @@ impl TopLevelComposer {
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ast::StmtKind::Assign { .. } => {
|
||||||
|
// Assignment statements can assign to (and therefore create) more than one
|
||||||
|
// variable, but this function only allows returning one set of symbol information.
|
||||||
|
// We want to avoid changing this to return a `Vec` of symbol info, as this would
|
||||||
|
// require `iter().next().unwrap()` on every variable created from a non-Assign
|
||||||
|
// statement.
|
||||||
|
//
|
||||||
|
// Make callers use `register_top_level_var` instead, as it provides more
|
||||||
|
// fine-grained control over which symbols to register, while also simplifying the
|
||||||
|
// usage of this function.
|
||||||
|
panic!("Registration of top-level Assign statements must use TopLevelComposer::register_top_level_var (at {})", ast.location);
|
||||||
|
}
|
||||||
|
|
||||||
ast::StmtKind::AnnAssign { target, annotation, .. } => {
|
ast::StmtKind::AnnAssign { target, annotation, .. } => {
|
||||||
let ExprKind::Name { id: name, .. } = target.node else {
|
let ExprKind::Name { id: name, .. } = target.node else {
|
||||||
return Err(format!(
|
return Err(format!(
|
||||||
"global variable declaration must be an identifier (at {})",
|
"global variable declaration must be an identifier (at {})",
|
||||||
ast.location
|
target.location
|
||||||
));
|
));
|
||||||
};
|
};
|
||||||
|
|
||||||
if self.keyword_list.contains(&name) {
|
self.register_top_level_var(
|
||||||
return Err(format!(
|
|
||||||
"cannot use keyword `{}` as a class name (at {})",
|
|
||||||
name,
|
name,
|
||||||
ast.location
|
Some(annotation.as_ref().clone()),
|
||||||
));
|
resolver,
|
||||||
|
mod_path,
|
||||||
|
target.location,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
let global_var_name = if mod_path.is_empty() {
|
_ => Err(format!(
|
||||||
name.to_string()
|
"registrations of constructs other than top level classes/functions/variables are not supported (at {})",
|
||||||
} else {
|
|
||||||
format!("{mod_path}.{name}")
|
|
||||||
};
|
|
||||||
if !defined_names.insert(global_var_name.clone()) {
|
|
||||||
return Err(format!(
|
|
||||||
"global variable `{}` defined twice (at {})",
|
|
||||||
global_var_name,
|
|
||||||
ast.location
|
ast.location
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Registers a top-level variable with the given `name` into the composer.
|
||||||
|
///
|
||||||
|
/// `annotation` - The type annotation of the top-level variable, or [`None`] if no type
|
||||||
|
/// annotation is provided.
|
||||||
|
/// `location` - The location of the top-level variable.
|
||||||
|
pub fn register_top_level_var(
|
||||||
|
&mut self,
|
||||||
|
name: Ident,
|
||||||
|
annotation: Option<Expr>,
|
||||||
|
resolver: Option<Arc<dyn SymbolResolver + Send + Sync>>,
|
||||||
|
mod_path: &str,
|
||||||
|
location: Location,
|
||||||
|
) -> Result<(StrRef, DefinitionId, Option<Type>), String> {
|
||||||
|
if self.keyword_list.contains(&name) {
|
||||||
|
return Err(format!("cannot use keyword `{name}` as a class name (at {location})"));
|
||||||
|
}
|
||||||
|
|
||||||
|
let global_var_name =
|
||||||
|
if mod_path.is_empty() { name.to_string() } else { format!("{mod_path}.{name}") };
|
||||||
|
|
||||||
|
if !self.defined_names.insert(global_var_name.clone()) {
|
||||||
|
return Err(format!(
|
||||||
|
"global variable `{global_var_name}` defined twice (at {location})"
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -418,25 +452,15 @@ impl TopLevelComposer {
|
|||||||
name,
|
name,
|
||||||
// dummy here, unify with correct type later,
|
// dummy here, unify with correct type later,
|
||||||
ty_to_be_unified,
|
ty_to_be_unified,
|
||||||
*(annotation.clone()),
|
annotation,
|
||||||
resolver,
|
resolver,
|
||||||
Some(ast.location),
|
Some(location),
|
||||||
)).into(),
|
))
|
||||||
|
.into(),
|
||||||
None,
|
None,
|
||||||
));
|
));
|
||||||
|
|
||||||
Ok((
|
Ok((name, DefinitionId(self.definition_ast_list.len() - 1), Some(ty_to_be_unified)))
|
||||||
name,
|
|
||||||
DefinitionId(self.definition_ast_list.len() - 1),
|
|
||||||
Some(ty_to_be_unified),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
_ => Err(format!(
|
|
||||||
"registrations of constructs other than top level classes/functions/variables are not supported (at {})",
|
|
||||||
ast.location
|
|
||||||
)),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn start_analysis(&mut self, inference: bool) -> Result<(), HashSet<String>> {
|
pub fn start_analysis(&mut self, inference: bool) -> Result<(), HashSet<String>> {
|
||||||
@ -485,7 +509,7 @@ impl TopLevelComposer {
|
|||||||
// things like `class A(Generic[T, V, ImportedModule.T])` is not supported
|
// things like `class A(Generic[T, V, ImportedModule.T])` is not supported
|
||||||
// i.e. only simple names are allowed in the subscript
|
// i.e. only simple names are allowed in the subscript
|
||||||
// should update the TopLevelDef::Class.typevars and the TypeEnum::TObj.params
|
// should update the TopLevelDef::Class.typevars and the TypeEnum::TObj.params
|
||||||
ast::ExprKind::Subscript { value, slice, .. }
|
ExprKind::Subscript { value, slice, .. }
|
||||||
if {
|
if {
|
||||||
matches!(
|
matches!(
|
||||||
&value.node,
|
&value.node,
|
||||||
@ -501,9 +525,9 @@ impl TopLevelComposer {
|
|||||||
}
|
}
|
||||||
is_generic = true;
|
is_generic = true;
|
||||||
|
|
||||||
let type_var_list: Vec<&ast::Expr<()>>;
|
let type_var_list: Vec<&Expr<()>>;
|
||||||
// if `class A(Generic[T, V, G])`
|
// if `class A(Generic[T, V, G])`
|
||||||
if let ast::ExprKind::Tuple { elts, .. } = &slice.node {
|
if let ExprKind::Tuple { elts, .. } = &slice.node {
|
||||||
type_var_list = elts.iter().collect_vec();
|
type_var_list = elts.iter().collect_vec();
|
||||||
// `class A(Generic[T])`
|
// `class A(Generic[T])`
|
||||||
} else {
|
} else {
|
||||||
@ -1014,15 +1038,15 @@ impl TopLevelComposer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let arg_with_default: Vec<(&ast::Located<ast::ArgData<()>>, Option<&ast::Expr>)> =
|
let arg_with_default: Vec<(&ast::Located<ast::ArgData<()>>, Option<&Expr>)> = args
|
||||||
args.args
|
.args
|
||||||
.iter()
|
.iter()
|
||||||
.rev()
|
.rev()
|
||||||
.zip(
|
.zip(
|
||||||
args.defaults
|
args.defaults
|
||||||
.iter()
|
.iter()
|
||||||
.rev()
|
.rev()
|
||||||
.map(|x| -> Option<&ast::Expr> { Some(x) })
|
.map(|x| -> Option<&Expr> { Some(x) })
|
||||||
.chain(std::iter::repeat(None)),
|
.chain(std::iter::repeat(None)),
|
||||||
)
|
)
|
||||||
.collect_vec();
|
.collect_vec();
|
||||||
@ -1283,7 +1307,7 @@ impl TopLevelComposer {
|
|||||||
|
|
||||||
let arg_with_default: Vec<(
|
let arg_with_default: Vec<(
|
||||||
&ast::Located<ast::ArgData<()>>,
|
&ast::Located<ast::ArgData<()>>,
|
||||||
Option<&ast::Expr>,
|
Option<&Expr>,
|
||||||
)> = args
|
)> = args
|
||||||
.args
|
.args
|
||||||
.iter()
|
.iter()
|
||||||
@ -1292,7 +1316,7 @@ impl TopLevelComposer {
|
|||||||
args.defaults
|
args.defaults
|
||||||
.iter()
|
.iter()
|
||||||
.rev()
|
.rev()
|
||||||
.map(|x| -> Option<&ast::Expr> { Some(x) })
|
.map(|x| -> Option<&Expr> { Some(x) })
|
||||||
.chain(std::iter::repeat(None)),
|
.chain(std::iter::repeat(None)),
|
||||||
)
|
)
|
||||||
.collect_vec();
|
.collect_vec();
|
||||||
@ -1449,7 +1473,7 @@ impl TopLevelComposer {
|
|||||||
.map_err(|e| HashSet::from([e.to_display(unifier).to_string()]))?;
|
.map_err(|e| HashSet::from([e.to_display(unifier).to_string()]))?;
|
||||||
}
|
}
|
||||||
ast::StmtKind::AnnAssign { target, annotation, value, .. } => {
|
ast::StmtKind::AnnAssign { target, annotation, value, .. } => {
|
||||||
if let ast::ExprKind::Name { id: attr, .. } = &target.node {
|
if let ExprKind::Name { id: attr, .. } = &target.node {
|
||||||
if defined_fields.insert(attr.to_string()) {
|
if defined_fields.insert(attr.to_string()) {
|
||||||
let dummy_field_type = unifier.get_dummy_var().ty;
|
let dummy_field_type = unifier.get_dummy_var().ty;
|
||||||
|
|
||||||
@ -1457,7 +1481,7 @@ impl TopLevelComposer {
|
|||||||
None => {
|
None => {
|
||||||
// handle Kernel[T], KernelInvariant[T]
|
// handle Kernel[T], KernelInvariant[T]
|
||||||
let (annotation, mutable) = match &annotation.node {
|
let (annotation, mutable) = match &annotation.node {
|
||||||
ast::ExprKind::Subscript { value, slice, .. }
|
ExprKind::Subscript { value, slice, .. }
|
||||||
if matches!(
|
if matches!(
|
||||||
&value.node,
|
&value.node,
|
||||||
ast::ExprKind::Name { id, .. } if id == &core_config.kernel_invariant_ann.into()
|
ast::ExprKind::Name { id, .. } if id == &core_config.kernel_invariant_ann.into()
|
||||||
@ -1465,7 +1489,7 @@ impl TopLevelComposer {
|
|||||||
{
|
{
|
||||||
(slice, false)
|
(slice, false)
|
||||||
}
|
}
|
||||||
ast::ExprKind::Subscript { value, slice, .. }
|
ExprKind::Subscript { value, slice, .. }
|
||||||
if matches!(
|
if matches!(
|
||||||
&value.node,
|
&value.node,
|
||||||
ast::ExprKind::Name { id, .. } if core_config.kernel_ann.map_or(false, |c| id == &c.into())
|
ast::ExprKind::Name { id, .. } if core_config.kernel_ann.map_or(false, |c| id == &c.into())
|
||||||
@ -1483,13 +1507,13 @@ impl TopLevelComposer {
|
|||||||
Some(boxed_expr) => {
|
Some(boxed_expr) => {
|
||||||
// Class attributes are set as immutable regardless
|
// Class attributes are set as immutable regardless
|
||||||
let (annotation, _) = match &annotation.node {
|
let (annotation, _) = match &annotation.node {
|
||||||
ast::ExprKind::Subscript { slice, .. } => (slice, false),
|
ExprKind::Subscript { slice, .. } => (slice, false),
|
||||||
_ if core_config.kernel_ann.is_none() => (annotation, false),
|
_ if core_config.kernel_ann.is_none() => (annotation, false),
|
||||||
_ => continue,
|
_ => continue,
|
||||||
};
|
};
|
||||||
|
|
||||||
match &**boxed_expr {
|
match &**boxed_expr {
|
||||||
ast::Located {location: _, custom: (), node: ast::ExprKind::Constant { value: v, kind: _ }} => {
|
ast::Located {location: _, custom: (), node: ExprKind::Constant { value: v, kind: _ }} => {
|
||||||
// Restricting the types allowed to be defined as class attributes
|
// Restricting the types allowed to be defined as class attributes
|
||||||
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(_) => {}
|
||||||
@ -1937,20 +1961,20 @@ impl TopLevelComposer {
|
|||||||
if ast.is_none() {
|
if ast.is_none() {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
let mut function_def = def.write();
|
|
||||||
if let TopLevelDef::Function {
|
let (name, simple_name, signature, resolver) = {
|
||||||
instance_to_stmt,
|
let function_def = def.read();
|
||||||
instance_to_symbol,
|
let TopLevelDef::Function { name, simple_name, signature, resolver, .. } =
|
||||||
name,
|
&*function_def
|
||||||
simple_name,
|
else {
|
||||||
signature,
|
return Ok(());
|
||||||
resolver,
|
};
|
||||||
..
|
|
||||||
} = &mut *function_def
|
(name.clone(), *simple_name, *signature, resolver.clone())
|
||||||
{
|
};
|
||||||
let signature_ty_enum = unifier.get_ty(*signature);
|
|
||||||
let TypeEnum::TFunc(FunSignature { args, ret, vars, .. }) =
|
let signature_ty_enum = unifier.get_ty(signature);
|
||||||
signature_ty_enum.as_ref()
|
let TypeEnum::TFunc(FunSignature { args, ret, vars, .. }) = signature_ty_enum.as_ref()
|
||||||
else {
|
else {
|
||||||
unreachable!("must be typeenum::tfunc")
|
unreachable!("must be typeenum::tfunc")
|
||||||
};
|
};
|
||||||
@ -2062,8 +2086,7 @@ impl TopLevelComposer {
|
|||||||
if self_type.is_some() {
|
if self_type.is_some() {
|
||||||
result.insert("self".into(), IdentifierInfo::default());
|
result.insert("self".into(), IdentifierInfo::default());
|
||||||
}
|
}
|
||||||
result
|
result.extend(inst_args.iter().map(|x| (x.name, IdentifierInfo::default())));
|
||||||
.extend(inst_args.iter().map(|x| (x.name, IdentifierInfo::default())));
|
|
||||||
result
|
result
|
||||||
};
|
};
|
||||||
let mut calls: HashMap<CodeLocation, CallId> = HashMap::new();
|
let mut calls: HashMap<CodeLocation, CallId> = HashMap::new();
|
||||||
@ -2100,33 +2123,43 @@ impl TopLevelComposer {
|
|||||||
else {
|
else {
|
||||||
unreachable!("must be function def ast")
|
unreachable!("must be function def ast")
|
||||||
};
|
};
|
||||||
if !decorator_list.is_empty()
|
|
||||||
&& matches!(&decorator_list[0].node,
|
|
||||||
ast::ExprKind::Name{ id, .. } if id == &"extern".into())
|
|
||||||
{
|
|
||||||
instance_to_symbol.insert(String::new(), simple_name.to_string());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if !decorator_list.is_empty()
|
|
||||||
&& matches!(&decorator_list[0].node,
|
|
||||||
ast::ExprKind::Name{ id, .. } if id == &"rpc".into())
|
|
||||||
{
|
|
||||||
instance_to_symbol.insert(String::new(), simple_name.to_string());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if !decorator_list.is_empty() {
|
if !decorator_list.is_empty() {
|
||||||
if let ast::ExprKind::Call { func, .. } = &decorator_list[0].node {
|
if matches!(&decorator_list[0].node, ExprKind::Name { id, .. } if id == &"extern".into())
|
||||||
if matches!(&func.node,
|
|
||||||
ast::ExprKind::Name{ id, .. } if id == &"rpc".into())
|
|
||||||
{
|
{
|
||||||
|
let TopLevelDef::Function { instance_to_symbol, .. } = &mut *def.write()
|
||||||
|
else {
|
||||||
|
unreachable!()
|
||||||
|
};
|
||||||
|
instance_to_symbol.insert(String::new(), simple_name.to_string());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if matches!(&decorator_list[0].node, ExprKind::Name { id, .. } if id == &"rpc".into())
|
||||||
|
{
|
||||||
|
let TopLevelDef::Function { instance_to_symbol, .. } = &mut *def.write()
|
||||||
|
else {
|
||||||
|
unreachable!()
|
||||||
|
};
|
||||||
|
instance_to_symbol.insert(String::new(), simple_name.to_string());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let ExprKind::Call { func, .. } = &decorator_list[0].node {
|
||||||
|
if matches!(&func.node, ExprKind::Name { id, .. } if id == &"rpc".into()) {
|
||||||
|
let TopLevelDef::Function { instance_to_symbol, .. } =
|
||||||
|
&mut *def.write()
|
||||||
|
else {
|
||||||
|
unreachable!()
|
||||||
|
};
|
||||||
instance_to_symbol.insert(String::new(), simple_name.to_string());
|
instance_to_symbol.insert(String::new(), simple_name.to_string());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let fun_body = body
|
let fun_body =
|
||||||
.into_iter()
|
body.into_iter()
|
||||||
.map(|b| inferencer.fold_stmt(b))
|
.map(|b| inferencer.fold_stmt(b))
|
||||||
.collect::<Result<Vec<_>, _>>()?;
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
|
|
||||||
@ -2196,6 +2229,9 @@ impl TopLevelComposer {
|
|||||||
)]));
|
)]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let TopLevelDef::Function { instance_to_stmt, .. } = &mut *def.write() else {
|
||||||
|
unreachable!()
|
||||||
|
};
|
||||||
instance_to_stmt.insert(
|
instance_to_stmt.insert(
|
||||||
get_subst_key(
|
get_subst_key(
|
||||||
unifier,
|
unifier,
|
||||||
@ -2211,10 +2247,10 @@ impl TopLevelComposer {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
};
|
};
|
||||||
|
|
||||||
for (id, (def, ast)) in self.definition_ast_list.iter().enumerate().skip(self.builtin_num) {
|
for (id, (def, ast)) in self.definition_ast_list.iter().enumerate().skip(self.builtin_num) {
|
||||||
if ast.is_none() {
|
if ast.is_none() {
|
||||||
continue;
|
continue;
|
||||||
@ -2237,9 +2273,8 @@ impl TopLevelComposer {
|
|||||||
let primitives_store = &self.primitives_ty;
|
let primitives_store = &self.primitives_ty;
|
||||||
|
|
||||||
let mut analyze = |variable_def: &Arc<RwLock<TopLevelDef>>| -> Result<_, HashSet<String>> {
|
let mut analyze = |variable_def: &Arc<RwLock<TopLevelDef>>| -> Result<_, HashSet<String>> {
|
||||||
let variable_def = &mut *variable_def.write();
|
let TopLevelDef::Variable { ty: dummy_ty, ty_decl, resolver, loc, .. } =
|
||||||
|
&*variable_def.read()
|
||||||
let TopLevelDef::Variable { ty: dummy_ty, ty_decl, resolver, loc, .. } = variable_def
|
|
||||||
else {
|
else {
|
||||||
// not top level variable def, skip
|
// not top level variable def, skip
|
||||||
return Ok(());
|
return Ok(());
|
||||||
@ -2247,6 +2282,7 @@ impl TopLevelComposer {
|
|||||||
|
|
||||||
let resolver = &**resolver.as_ref().unwrap();
|
let resolver = &**resolver.as_ref().unwrap();
|
||||||
|
|
||||||
|
if let Some(ty_decl) = ty_decl {
|
||||||
let ty_annotation = parse_ast_to_type_annotation_kinds(
|
let ty_annotation = parse_ast_to_type_annotation_kinds(
|
||||||
resolver,
|
resolver,
|
||||||
&temp_def_list,
|
&temp_def_list,
|
||||||
@ -2266,6 +2302,8 @@ impl TopLevelComposer {
|
|||||||
unifier.unify(*dummy_ty, ty_from_ty_annotation).map_err(|e| {
|
unifier.unify(*dummy_ty, ty_from_ty_annotation).map_err(|e| {
|
||||||
HashSet::from([e.at(Some(loc.unwrap())).to_display(unifier).to_string()])
|
HashSet::from([e.at(Some(loc.unwrap())).to_display(unifier).to_string()])
|
||||||
})?;
|
})?;
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -600,7 +600,7 @@ impl TopLevelComposer {
|
|||||||
name: String,
|
name: String,
|
||||||
simple_name: StrRef,
|
simple_name: StrRef,
|
||||||
ty: Type,
|
ty: Type,
|
||||||
ty_decl: Expr,
|
ty_decl: Option<Expr>,
|
||||||
resolver: Option<Arc<dyn SymbolResolver + Send + Sync>>,
|
resolver: Option<Arc<dyn SymbolResolver + Send + Sync>>,
|
||||||
loc: Option<Location>,
|
loc: Option<Location>,
|
||||||
) -> TopLevelDef {
|
) -> TopLevelDef {
|
||||||
|
@ -158,8 +158,8 @@ pub enum TopLevelDef {
|
|||||||
/// Type of the global variable.
|
/// Type of the global variable.
|
||||||
ty: Type,
|
ty: Type,
|
||||||
|
|
||||||
/// The declared type of the global variable.
|
/// The declared type of the global variable, or [`None`] if no type annotation is provided.
|
||||||
ty_decl: Expr,
|
ty_decl: Option<Expr>,
|
||||||
|
|
||||||
/// Symbol resolver of the module defined the class.
|
/// Symbol resolver of the module defined the class.
|
||||||
resolver: Option<Arc<dyn SymbolResolver + Send + Sync>>,
|
resolver: Option<Arc<dyn SymbolResolver + Send + Sync>>,
|
||||||
|
@ -10,7 +10,7 @@ use nac3parser::ast::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
type_inferencer::{IdentifierInfo, Inferencer},
|
type_inferencer::{DeclarationSource, IdentifierInfo, Inferencer},
|
||||||
typedef::{Type, TypeEnum},
|
typedef::{Type, TypeEnum},
|
||||||
};
|
};
|
||||||
use crate::toplevel::helper::PrimDef;
|
use crate::toplevel::helper::PrimDef;
|
||||||
@ -34,6 +34,20 @@ impl<'a> Inferencer<'a> {
|
|||||||
Err(HashSet::from([format!("cannot assign to a `none` (at {})", pattern.location)]))
|
Err(HashSet::from([format!("cannot assign to a `none` (at {})", pattern.location)]))
|
||||||
}
|
}
|
||||||
ExprKind::Name { id, .. } => {
|
ExprKind::Name { id, .. } => {
|
||||||
|
// If `id` refers to a declared symbol, reject this assignment if it is used in the
|
||||||
|
// context of an (implicit) global variable
|
||||||
|
if let Some(id_info) = defined_identifiers.get(id) {
|
||||||
|
if matches!(
|
||||||
|
id_info.source,
|
||||||
|
DeclarationSource::Global { is_explicit: Some(false) }
|
||||||
|
) {
|
||||||
|
return Err(HashSet::from([format!(
|
||||||
|
"cannot access local variable '{id}' before it is declared (at {})",
|
||||||
|
pattern.location
|
||||||
|
)]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if !defined_identifiers.contains_key(id) {
|
if !defined_identifiers.contains_key(id) {
|
||||||
defined_identifiers.insert(*id, IdentifierInfo::default());
|
defined_identifiers.insert(*id, IdentifierInfo::default());
|
||||||
}
|
}
|
||||||
@ -104,7 +118,22 @@ impl<'a> Inferencer<'a> {
|
|||||||
*id,
|
*id,
|
||||||
) {
|
) {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
self.defined_identifiers.insert(*id, IdentifierInfo::default());
|
let is_global = self.is_id_global(*id);
|
||||||
|
|
||||||
|
defined_identifiers.insert(
|
||||||
|
*id,
|
||||||
|
IdentifierInfo {
|
||||||
|
source: match is_global {
|
||||||
|
Some(true) => {
|
||||||
|
DeclarationSource::Global { is_explicit: Some(false) }
|
||||||
|
}
|
||||||
|
Some(false) => {
|
||||||
|
DeclarationSource::Global { is_explicit: None }
|
||||||
|
}
|
||||||
|
None => DeclarationSource::Local,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
return Err(HashSet::from([format!(
|
return Err(HashSet::from([format!(
|
||||||
@ -368,9 +397,9 @@ impl<'a> Inferencer<'a> {
|
|||||||
StmtKind::Global { names, .. } => {
|
StmtKind::Global { names, .. } => {
|
||||||
for id in names {
|
for id in names {
|
||||||
if let Some(id_info) = defined_identifiers.get(id) {
|
if let Some(id_info) = defined_identifiers.get(id) {
|
||||||
if !id_info.is_global {
|
if id_info.source == DeclarationSource::Local {
|
||||||
return Err(HashSet::from([format!(
|
return Err(HashSet::from([format!(
|
||||||
"name '{id}' is assigned to before global declaration at {}",
|
"name '{id}' is referenced prior to global declaration at {}",
|
||||||
stmt.location,
|
stmt.location,
|
||||||
)]));
|
)]));
|
||||||
}
|
}
|
||||||
@ -385,8 +414,12 @@ impl<'a> Inferencer<'a> {
|
|||||||
*id,
|
*id,
|
||||||
) {
|
) {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
self.defined_identifiers
|
defined_identifiers.insert(
|
||||||
.insert(*id, IdentifierInfo { is_global: true });
|
*id,
|
||||||
|
IdentifierInfo {
|
||||||
|
source: DeclarationSource::Global { is_explicit: Some(true) },
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
return Err(HashSet::from([format!(
|
return Err(HashSet::from([format!(
|
||||||
|
@ -12,7 +12,7 @@ use itertools::{izip, Itertools};
|
|||||||
use nac3parser::ast::{
|
use nac3parser::ast::{
|
||||||
self,
|
self,
|
||||||
fold::{self, Fold},
|
fold::{self, Fold},
|
||||||
Arguments, Comprehension, ExprContext, ExprKind, Located, Location, StrRef,
|
Arguments, Comprehension, ExprContext, ExprKind, Ident, Located, Location, StrRef,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
@ -88,11 +88,31 @@ impl PrimitiveStore {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The location where an identifier declaration refers to.
|
||||||
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||||
|
pub enum DeclarationSource {
|
||||||
|
/// Local scope.
|
||||||
|
Local,
|
||||||
|
|
||||||
|
/// Global scope.
|
||||||
|
Global {
|
||||||
|
/// Whether the identifier is declared by the use of `global` statement. This field is
|
||||||
|
/// [`None`] if the identifier does not refer to a variable.
|
||||||
|
is_explicit: Option<bool>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
/// Information regarding a defined identifier.
|
/// Information regarding a defined identifier.
|
||||||
#[derive(Clone, Copy, Debug, Default)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub struct IdentifierInfo {
|
pub struct IdentifierInfo {
|
||||||
/// Whether this identifier refers to a global variable.
|
/// Whether this identifier refers to a global variable.
|
||||||
pub is_global: bool,
|
pub source: DeclarationSource,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for IdentifierInfo {
|
||||||
|
fn default() -> Self {
|
||||||
|
IdentifierInfo { source: DeclarationSource::Local }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IdentifierInfo {
|
impl IdentifierInfo {
|
||||||
@ -574,7 +594,22 @@ impl<'a> Fold<()> for Inferencer<'a> {
|
|||||||
*id,
|
*id,
|
||||||
) {
|
) {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
self.defined_identifiers.insert(*id, IdentifierInfo::default());
|
let is_global = self.is_id_global(*id);
|
||||||
|
|
||||||
|
self.defined_identifiers.insert(
|
||||||
|
*id,
|
||||||
|
IdentifierInfo {
|
||||||
|
source: match is_global {
|
||||||
|
Some(true) => DeclarationSource::Global {
|
||||||
|
is_explicit: Some(false),
|
||||||
|
},
|
||||||
|
Some(false) => {
|
||||||
|
DeclarationSource::Global { is_explicit: None }
|
||||||
|
}
|
||||||
|
None => DeclarationSource::Local,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
return report_error(
|
return report_error(
|
||||||
@ -2650,4 +2685,22 @@ impl<'a> Inferencer<'a> {
|
|||||||
self.constrain(body.custom.unwrap(), orelse.custom.unwrap(), &body.location)?;
|
self.constrain(body.custom.unwrap(), orelse.custom.unwrap(), &body.location)?;
|
||||||
Ok(body.custom.unwrap())
|
Ok(body.custom.unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Determines whether the given `id` refers to a global symbol.
|
||||||
|
///
|
||||||
|
/// Returns `Some(true)` if `id` refers to a global variable, `Some(false)` if `id` refers to a
|
||||||
|
/// class/function, and `None` if `id` refers to a local symbol.
|
||||||
|
pub(super) fn is_id_global(&self, id: Ident) -> Option<bool> {
|
||||||
|
self.top_level
|
||||||
|
.definitions
|
||||||
|
.read()
|
||||||
|
.iter()
|
||||||
|
.map(|def| match *def.read() {
|
||||||
|
TopLevelDef::Class { name, .. } => (name, false),
|
||||||
|
TopLevelDef::Function { simple_name, .. } => (simple_name, false),
|
||||||
|
TopLevelDef::Variable { simple_name, .. } => (simple_name, true),
|
||||||
|
})
|
||||||
|
.find(|(global, _)| global == &id)
|
||||||
|
.map(|(_, has_explicit_prop)| has_explicit_prop)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@ def output_int64(x: int64):
|
|||||||
...
|
...
|
||||||
|
|
||||||
X: int32 = 0
|
X: int32 = 0
|
||||||
Y: int64 = int64(1)
|
Y = int64(1)
|
||||||
|
|
||||||
def f():
|
def f():
|
||||||
global X, Y
|
global X, Y
|
||||||
|
@ -174,46 +174,49 @@ fn handle_typevar_definition(
|
|||||||
fn handle_assignment_pattern(
|
fn handle_assignment_pattern(
|
||||||
targets: &[Expr],
|
targets: &[Expr],
|
||||||
value: &Expr,
|
value: &Expr,
|
||||||
resolver: &(dyn SymbolResolver + Send + Sync),
|
resolver: Arc<dyn SymbolResolver + Send + Sync>,
|
||||||
internal_resolver: &ResolverInternal,
|
internal_resolver: &ResolverInternal,
|
||||||
def_list: &[Arc<RwLock<TopLevelDef>>],
|
composer: &mut TopLevelComposer,
|
||||||
unifier: &mut Unifier,
|
|
||||||
primitives: &PrimitiveStore,
|
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
if targets.len() == 1 {
|
if targets.len() == 1 {
|
||||||
match &targets[0].node {
|
let target = &targets[0];
|
||||||
|
|
||||||
|
match &target.node {
|
||||||
ExprKind::Name { id, .. } => {
|
ExprKind::Name { id, .. } => {
|
||||||
|
let def_list = composer.extract_def_list();
|
||||||
|
let unifier = &mut composer.unifier;
|
||||||
|
let primitives = &composer.primitives_ty;
|
||||||
|
|
||||||
if let Ok(var) =
|
if let Ok(var) =
|
||||||
handle_typevar_definition(value, resolver, def_list, unifier, primitives)
|
handle_typevar_definition(value, &*resolver, &def_list, unifier, primitives)
|
||||||
{
|
{
|
||||||
internal_resolver.add_id_type(*id, var);
|
internal_resolver.add_id_type(*id, var);
|
||||||
Ok(())
|
Ok(())
|
||||||
} else if let Ok(val) = parse_parameter_default_value(value, resolver) {
|
} else if let Ok(val) = parse_parameter_default_value(value, &*resolver) {
|
||||||
internal_resolver.add_module_global(*id, val);
|
internal_resolver.add_module_global(*id, val);
|
||||||
|
let (name, def_id, _) = composer
|
||||||
|
.register_top_level_var(
|
||||||
|
*id,
|
||||||
|
None,
|
||||||
|
Some(resolver.clone()),
|
||||||
|
"__main__",
|
||||||
|
target.location,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
internal_resolver.add_id_def(name, def_id);
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(format!("fails to evaluate this expression `{:?}` as a constant or generic parameter at {}",
|
Err(format!("fails to evaluate this expression `{:?}` as a constant or generic parameter at {}",
|
||||||
targets[0].node,
|
target.node,
|
||||||
targets[0].location,
|
target.location,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ExprKind::List { elts, .. } | ExprKind::Tuple { elts, .. } => {
|
ExprKind::List { elts, .. } | ExprKind::Tuple { elts, .. } => {
|
||||||
handle_assignment_pattern(
|
handle_assignment_pattern(elts, value, resolver, internal_resolver, composer)?;
|
||||||
elts,
|
|
||||||
value,
|
|
||||||
resolver,
|
|
||||||
internal_resolver,
|
|
||||||
def_list,
|
|
||||||
unifier,
|
|
||||||
primitives,
|
|
||||||
)?;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
_ => Err(format!(
|
_ => Err(format!("assignment to {target:?} is not supported at {}", target.location)),
|
||||||
"assignment to {:?} is not supported at {}",
|
|
||||||
targets[0], targets[0].location
|
|
||||||
)),
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
match &value.node {
|
match &value.node {
|
||||||
@ -223,11 +226,9 @@ fn handle_assignment_pattern(
|
|||||||
handle_assignment_pattern(
|
handle_assignment_pattern(
|
||||||
std::slice::from_ref(tar),
|
std::slice::from_ref(tar),
|
||||||
val,
|
val,
|
||||||
resolver,
|
resolver.clone(),
|
||||||
internal_resolver,
|
internal_resolver,
|
||||||
def_list,
|
composer,
|
||||||
unifier,
|
|
||||||
primitives,
|
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -248,8 +249,9 @@ fn handle_assignment_pattern(
|
|||||||
fn handle_global_var(
|
fn handle_global_var(
|
||||||
target: &Expr,
|
target: &Expr,
|
||||||
value: Option<&Expr>,
|
value: Option<&Expr>,
|
||||||
resolver: &(dyn SymbolResolver + Send + Sync),
|
resolver: &Arc<dyn SymbolResolver + Send + Sync>,
|
||||||
internal_resolver: &ResolverInternal,
|
internal_resolver: &ResolverInternal,
|
||||||
|
composer: &mut TopLevelComposer,
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
let ExprKind::Name { id, .. } = target.node else {
|
let ExprKind::Name { id, .. } = target.node else {
|
||||||
return Err(format!(
|
return Err(format!(
|
||||||
@ -262,8 +264,12 @@ fn handle_global_var(
|
|||||||
return Err(format!("global variable `{id}` must be initialized in its definition"));
|
return Err(format!("global variable `{id}` must be initialized in its definition"));
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Ok(val) = parse_parameter_default_value(value, resolver) {
|
if let Ok(val) = parse_parameter_default_value(value, &**resolver) {
|
||||||
internal_resolver.add_module_global(id, val);
|
internal_resolver.add_module_global(id, val);
|
||||||
|
let (name, def_id, _) = composer
|
||||||
|
.register_top_level_var(id, None, Some(resolver.clone()), "__main__", target.location)
|
||||||
|
.unwrap();
|
||||||
|
internal_resolver.add_id_def(name, def_id);
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(format!(
|
Err(format!(
|
||||||
@ -355,17 +361,12 @@ fn main() {
|
|||||||
for stmt in parser_result {
|
for stmt in parser_result {
|
||||||
match &stmt.node {
|
match &stmt.node {
|
||||||
StmtKind::Assign { targets, value, .. } => {
|
StmtKind::Assign { targets, value, .. } => {
|
||||||
let def_list = composer.extract_def_list();
|
|
||||||
let unifier = &mut composer.unifier;
|
|
||||||
let primitives = &composer.primitives_ty;
|
|
||||||
if let Err(err) = handle_assignment_pattern(
|
if let Err(err) = handle_assignment_pattern(
|
||||||
targets,
|
targets,
|
||||||
value,
|
value,
|
||||||
resolver.as_ref(),
|
resolver.clone(),
|
||||||
internal_resolver.as_ref(),
|
internal_resolver.as_ref(),
|
||||||
&def_list,
|
&mut composer,
|
||||||
unifier,
|
|
||||||
primitives,
|
|
||||||
) {
|
) {
|
||||||
panic!("{err}");
|
panic!("{err}");
|
||||||
}
|
}
|
||||||
@ -375,16 +376,12 @@ fn main() {
|
|||||||
if let Err(err) = handle_global_var(
|
if let Err(err) = handle_global_var(
|
||||||
target,
|
target,
|
||||||
value.as_ref().map(Box::as_ref),
|
value.as_ref().map(Box::as_ref),
|
||||||
resolver.as_ref(),
|
&resolver,
|
||||||
internal_resolver.as_ref(),
|
internal_resolver.as_ref(),
|
||||||
|
&mut composer,
|
||||||
) {
|
) {
|
||||||
panic!("{err}");
|
panic!("{err}");
|
||||||
}
|
}
|
||||||
|
|
||||||
let (name, def_id, _) = composer
|
|
||||||
.register_top_level(stmt, Some(resolver.clone()), "__main__", true)
|
|
||||||
.unwrap();
|
|
||||||
internal_resolver.add_id_def(name, def_id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// allow (and ignore) "from __future__ import annotations"
|
// allow (and ignore) "from __future__ import annotations"
|
||||||
|
Loading…
Reference in New Issue
Block a user