Compare commits

...

9 Commits

8 changed files with 531 additions and 386 deletions

View File

@ -2886,7 +2886,31 @@ pub fn gen_expr<'ctx, G: CodeGenerator>(
Some((_, Some(static_value), _)) => ValueEnum::Static(static_value.clone()),
None => {
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, .. } => {

View File

@ -1,6 +1,6 @@
use std::rc::Rc;
use nac3parser::ast::{fold::Fold, ExprKind};
use nac3parser::ast::{fold::Fold, ExprKind, Ident};
use super::*;
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, .. } => {
let ExprKind::Name { id: name, .. } = target.node else {
return Err(format!(
"global variable declaration must be an identifier (at {})",
ast.location
target.location
));
};
if self.keyword_list.contains(&name) {
return Err(format!(
"cannot use keyword `{}` as a class name (at {})",
self.register_top_level_var(
name,
ast.location
));
Some(annotation.as_ref().clone()),
resolver,
mod_path,
target.location,
)
}
let global_var_name = if mod_path.is_empty() {
name.to_string()
} else {
format!("{mod_path}.{name}")
};
if !defined_names.insert(global_var_name.clone()) {
return Err(format!(
"global variable `{}` defined twice (at {})",
global_var_name,
_ => Err(format!(
"registrations of constructs other than top level classes/functions/variables are not supported (at {})",
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,
// dummy here, unify with correct type later,
ty_to_be_unified,
*(annotation.clone()),
annotation,
resolver,
Some(ast.location),
)).into(),
Some(location),
))
.into(),
None,
));
Ok((
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
)),
}
Ok((name, DefinitionId(self.definition_ast_list.len() - 1), Some(ty_to_be_unified)))
}
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
// i.e. only simple names are allowed in the subscript
// should update the TopLevelDef::Class.typevars and the TypeEnum::TObj.params
ast::ExprKind::Subscript { value, slice, .. }
ExprKind::Subscript { value, slice, .. }
if {
matches!(
&value.node,
@ -501,9 +525,9 @@ impl TopLevelComposer {
}
is_generic = true;
let type_var_list: Vec<&ast::Expr<()>>;
let type_var_list: Vec<&Expr<()>>;
// 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();
// `class A(Generic[T])`
} else {
@ -1014,15 +1038,15 @@ impl TopLevelComposer {
}
}
let arg_with_default: Vec<(&ast::Located<ast::ArgData<()>>, Option<&ast::Expr>)> =
args.args
let arg_with_default: Vec<(&ast::Located<ast::ArgData<()>>, Option<&Expr>)> = args
.args
.iter()
.rev()
.zip(
args.defaults
.iter()
.rev()
.map(|x| -> Option<&ast::Expr> { Some(x) })
.map(|x| -> Option<&Expr> { Some(x) })
.chain(std::iter::repeat(None)),
)
.collect_vec();
@ -1283,7 +1307,7 @@ impl TopLevelComposer {
let arg_with_default: Vec<(
&ast::Located<ast::ArgData<()>>,
Option<&ast::Expr>,
Option<&Expr>,
)> = args
.args
.iter()
@ -1292,7 +1316,7 @@ impl TopLevelComposer {
args.defaults
.iter()
.rev()
.map(|x| -> Option<&ast::Expr> { Some(x) })
.map(|x| -> Option<&Expr> { Some(x) })
.chain(std::iter::repeat(None)),
)
.collect_vec();
@ -1449,7 +1473,7 @@ impl TopLevelComposer {
.map_err(|e| HashSet::from([e.to_display(unifier).to_string()]))?;
}
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()) {
let dummy_field_type = unifier.get_dummy_var().ty;
@ -1457,7 +1481,7 @@ impl TopLevelComposer {
None => {
// handle Kernel[T], KernelInvariant[T]
let (annotation, mutable) = match &annotation.node {
ast::ExprKind::Subscript { value, slice, .. }
ExprKind::Subscript { value, slice, .. }
if matches!(
&value.node,
ast::ExprKind::Name { id, .. } if id == &core_config.kernel_invariant_ann.into()
@ -1465,7 +1489,7 @@ impl TopLevelComposer {
{
(slice, false)
}
ast::ExprKind::Subscript { value, slice, .. }
ExprKind::Subscript { value, slice, .. }
if matches!(
&value.node,
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) => {
// Class attributes are set as immutable regardless
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),
_ => continue,
};
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
match v {
ast::Constant::Bool(_) | ast::Constant::Str(_) | ast::Constant::Int(_) | ast::Constant::Float(_) => {}
@ -1937,20 +1961,20 @@ impl TopLevelComposer {
if ast.is_none() {
return Ok(());
}
let mut function_def = def.write();
if let TopLevelDef::Function {
instance_to_stmt,
instance_to_symbol,
name,
simple_name,
signature,
resolver,
..
} = &mut *function_def
{
let signature_ty_enum = unifier.get_ty(*signature);
let TypeEnum::TFunc(FunSignature { args, ret, vars, .. }) =
signature_ty_enum.as_ref()
let (name, simple_name, signature, resolver) = {
let function_def = def.read();
let TopLevelDef::Function { name, simple_name, signature, resolver, .. } =
&*function_def
else {
return Ok(());
};
(name.clone(), *simple_name, *signature, resolver.clone())
};
let signature_ty_enum = unifier.get_ty(signature);
let TypeEnum::TFunc(FunSignature { args, ret, vars, .. }) = signature_ty_enum.as_ref()
else {
unreachable!("must be typeenum::tfunc")
};
@ -2062,8 +2086,7 @@ impl TopLevelComposer {
if self_type.is_some() {
result.insert("self".into(), IdentifierInfo::default());
}
result
.extend(inst_args.iter().map(|x| (x.name, IdentifierInfo::default())));
result.extend(inst_args.iter().map(|x| (x.name, IdentifierInfo::default())));
result
};
let mut calls: HashMap<CodeLocation, CallId> = HashMap::new();
@ -2100,33 +2123,43 @@ impl TopLevelComposer {
else {
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 let ast::ExprKind::Call { func, .. } = &decorator_list[0].node {
if matches!(&func.node,
ast::ExprKind::Name{ id, .. } if id == &"rpc".into())
if matches!(&decorator_list[0].node, ExprKind::Name { id, .. } if id == &"extern".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());
continue;
}
}
}
let fun_body = body
.into_iter()
let fun_body =
body.into_iter()
.map(|b| inferencer.fold_stmt(b))
.collect::<Result<Vec<_>, _>>()?;
@ -2196,6 +2229,9 @@ impl TopLevelComposer {
)]));
}
let TopLevelDef::Function { instance_to_stmt, .. } = &mut *def.write() else {
unreachable!()
};
instance_to_stmt.insert(
get_subst_key(
unifier,
@ -2211,10 +2247,10 @@ impl TopLevelComposer {
},
);
}
}
Ok(())
};
for (id, (def, ast)) in self.definition_ast_list.iter().enumerate().skip(self.builtin_num) {
if ast.is_none() {
continue;
@ -2237,9 +2273,8 @@ impl TopLevelComposer {
let primitives_store = &self.primitives_ty;
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
let TopLevelDef::Variable { ty: dummy_ty, ty_decl, resolver, loc, .. } =
&*variable_def.read()
else {
// not top level variable def, skip
return Ok(());
@ -2247,6 +2282,7 @@ impl TopLevelComposer {
let resolver = &**resolver.as_ref().unwrap();
if let Some(ty_decl) = ty_decl {
let ty_annotation = parse_ast_to_type_annotation_kinds(
resolver,
&temp_def_list,
@ -2266,6 +2302,8 @@ impl TopLevelComposer {
unifier.unify(*dummy_ty, ty_from_ty_annotation).map_err(|e| {
HashSet::from([e.at(Some(loc.unwrap())).to_display(unifier).to_string()])
})?;
}
Ok(())
};

View File

@ -600,7 +600,7 @@ impl TopLevelComposer {
name: String,
simple_name: StrRef,
ty: Type,
ty_decl: Expr,
ty_decl: Option<Expr>,
resolver: Option<Arc<dyn SymbolResolver + Send + Sync>>,
loc: Option<Location>,
) -> TopLevelDef {

View File

@ -158,8 +158,8 @@ pub enum TopLevelDef {
/// Type of the global variable.
ty: Type,
/// The declared type of the global variable.
ty_decl: Expr,
/// The declared type of the global variable, or [`None`] if no type annotation is provided.
ty_decl: Option<Expr>,
/// Symbol resolver of the module defined the class.
resolver: Option<Arc<dyn SymbolResolver + Send + Sync>>,

View File

@ -10,7 +10,7 @@ use nac3parser::ast::{
};
use super::{
type_inferencer::{IdentifierInfo, Inferencer},
type_inferencer::{DeclarationSource, IdentifierInfo, Inferencer},
typedef::{Type, TypeEnum},
};
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)]))
}
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) {
defined_identifiers.insert(*id, IdentifierInfo::default());
}
@ -104,7 +118,22 @@ impl<'a> Inferencer<'a> {
*id,
) {
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) => {
return Err(HashSet::from([format!(
@ -368,9 +397,9 @@ impl<'a> Inferencer<'a> {
StmtKind::Global { names, .. } => {
for id in names {
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!(
"name '{id}' is assigned to before global declaration at {}",
"name '{id}' is referenced prior to global declaration at {}",
stmt.location,
)]));
}
@ -385,8 +414,12 @@ impl<'a> Inferencer<'a> {
*id,
) {
Ok(_) => {
self.defined_identifiers
.insert(*id, IdentifierInfo { is_global: true });
defined_identifiers.insert(
*id,
IdentifierInfo {
source: DeclarationSource::Global { is_explicit: Some(true) },
},
);
}
Err(e) => {
return Err(HashSet::from([format!(

View File

@ -12,7 +12,7 @@ use itertools::{izip, Itertools};
use nac3parser::ast::{
self,
fold::{self, Fold},
Arguments, Comprehension, ExprContext, ExprKind, Located, Location, StrRef,
Arguments, Comprehension, ExprContext, ExprKind, Ident, Located, Location, StrRef,
};
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.
#[derive(Clone, Copy, Debug, Default)]
#[derive(Clone, Copy, Debug)]
pub struct IdentifierInfo {
/// 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 {
@ -574,7 +594,22 @@ impl<'a> Fold<()> for Inferencer<'a> {
*id,
) {
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) => {
return report_error(
@ -2650,4 +2685,22 @@ impl<'a> Inferencer<'a> {
self.constrain(body.custom.unwrap(), orelse.custom.unwrap(), &body.location)?;
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)
}
}

View File

@ -7,7 +7,7 @@ def output_int64(x: int64):
...
X: int32 = 0
Y: int64 = int64(1)
Y = int64(1)
def f():
global X, Y

View File

@ -174,46 +174,49 @@ fn handle_typevar_definition(
fn handle_assignment_pattern(
targets: &[Expr],
value: &Expr,
resolver: &(dyn SymbolResolver + Send + Sync),
resolver: Arc<dyn SymbolResolver + Send + Sync>,
internal_resolver: &ResolverInternal,
def_list: &[Arc<RwLock<TopLevelDef>>],
unifier: &mut Unifier,
primitives: &PrimitiveStore,
composer: &mut TopLevelComposer,
) -> Result<(), String> {
if targets.len() == 1 {
match &targets[0].node {
let target = &targets[0];
match &target.node {
ExprKind::Name { id, .. } => {
let def_list = composer.extract_def_list();
let unifier = &mut composer.unifier;
let primitives = &composer.primitives_ty;
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);
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);
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(())
} else {
Err(format!("fails to evaluate this expression `{:?}` as a constant or generic parameter at {}",
targets[0].node,
targets[0].location,
target.node,
target.location,
))
}
}
ExprKind::List { elts, .. } | ExprKind::Tuple { elts, .. } => {
handle_assignment_pattern(
elts,
value,
resolver,
internal_resolver,
def_list,
unifier,
primitives,
)?;
handle_assignment_pattern(elts, value, resolver, internal_resolver, composer)?;
Ok(())
}
_ => Err(format!(
"assignment to {:?} is not supported at {}",
targets[0], targets[0].location
)),
_ => Err(format!("assignment to {target:?} is not supported at {}", target.location)),
}
} else {
match &value.node {
@ -223,11 +226,9 @@ fn handle_assignment_pattern(
handle_assignment_pattern(
std::slice::from_ref(tar),
val,
resolver,
resolver.clone(),
internal_resolver,
def_list,
unifier,
primitives,
composer,
)?;
}
Ok(())
@ -248,8 +249,9 @@ fn handle_assignment_pattern(
fn handle_global_var(
target: &Expr,
value: Option<&Expr>,
resolver: &(dyn SymbolResolver + Send + Sync),
resolver: &Arc<dyn SymbolResolver + Send + Sync>,
internal_resolver: &ResolverInternal,
composer: &mut TopLevelComposer,
) -> Result<(), String> {
let ExprKind::Name { id, .. } = target.node else {
return Err(format!(
@ -262,8 +264,12 @@ fn handle_global_var(
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);
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(())
} else {
Err(format!(
@ -355,17 +361,12 @@ fn main() {
for stmt in parser_result {
match &stmt.node {
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(
targets,
value,
resolver.as_ref(),
resolver.clone(),
internal_resolver.as_ref(),
&def_list,
unifier,
primitives,
&mut composer,
) {
panic!("{err}");
}
@ -375,16 +376,12 @@ fn main() {
if let Err(err) = handle_global_var(
target,
value.as_ref().map(Box::as_ref),
resolver.as_ref(),
&resolver,
internal_resolver.as_ref(),
&mut composer,
) {
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"