Fix several issues with global variables #547

Merged
sb10q merged 5 commits from enhance/global-var into master 2024-10-07 13:58:44 +08:00
4 changed files with 401 additions and 279 deletions

View File

@ -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, .. } => {

View File

@ -485,7 +485,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 +501,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,18 +1014,18 @@ 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();
arg_with_default arg_with_default
.iter() .iter()
@ -1283,7 +1283,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 +1292,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 +1449,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 +1457,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 +1465,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 +1483,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,284 +1937,296 @@ 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,
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()
else { else {
unreachable!("must be typeenum::tfunc") return Ok(());
}; };
let mut vars = vars.clone(); (name.clone(), *simple_name, *signature, resolver.clone())
// None if is not class method };
let uninst_self_type = {
if let Some(class_id) = method_class.get(&DefinitionId(id)) { let signature_ty_enum = unifier.get_ty(signature);
let class_def = definition_ast_list.get(class_id.0).unwrap(); let TypeEnum::TFunc(FunSignature { args, ret, vars, .. }) = signature_ty_enum.as_ref()
let class_def = class_def.0.read(); else {
let TopLevelDef::Class { type_vars, .. } = &*class_def else { unreachable!("must be typeenum::tfunc")
unreachable!("must be class def") };
let mut vars = vars.clone();
// None if is not class method
let uninst_self_type = {
if let Some(class_id) = method_class.get(&DefinitionId(id)) {
let class_def = definition_ast_list.get(class_id.0).unwrap();
let class_def = class_def.0.read();
let TopLevelDef::Class { type_vars, .. } = &*class_def else {
unreachable!("must be class def")
};
let ty_ann = make_self_type_annotation(type_vars, *class_id);
let self_ty = get_type_from_type_annotation_kinds(
&def_list,
unifier,
primitives_ty,
&ty_ann,
&mut None,
)?;
vars.extend(type_vars.iter().map(|ty| {
let TypeEnum::TVar { id, .. } = &*unifier.get_ty(*ty) else {
unreachable!()
}; };
let ty_ann = make_self_type_annotation(type_vars, *class_id); (*id, *ty)
let self_ty = get_type_from_type_annotation_kinds( }));
&def_list, Some((self_ty, type_vars.clone()))
unifier, } else {
primitives_ty, None
&ty_ann, }
&mut None, };
)?; // carefully handle those with bounds, without bounds and no typevars
vars.extend(type_vars.iter().map(|ty| { // if class methods, `vars` also contains all class typevars here
let TypeEnum::TVar { id, .. } = &*unifier.get_ty(*ty) else { let (type_var_subst_comb, no_range_vars) = {
let mut no_ranges: Vec<Type> = Vec::new();
let var_combs = vars
.values()
.map(|ty| {
unifier.get_instantiations(*ty).unwrap_or_else(|| {
let TypeEnum::TVar { name, loc, is_const_generic: false, .. } =
&*unifier.get_ty(*ty)
else {
unreachable!() unreachable!()
}; };
(*id, *ty) let rigid = unifier.get_fresh_rigid_var(*name, *loc).ty;
})); no_ranges.push(rigid);
Some((self_ty, type_vars.clone())) vec![rigid]
} else {
None
}
};
// carefully handle those with bounds, without bounds and no typevars
// if class methods, `vars` also contains all class typevars here
let (type_var_subst_comb, no_range_vars) = {
let mut no_ranges: Vec<Type> = Vec::new();
let var_combs = vars
.values()
.map(|ty| {
unifier.get_instantiations(*ty).unwrap_or_else(|| {
let TypeEnum::TVar { name, loc, is_const_generic: false, .. } =
&*unifier.get_ty(*ty)
else {
unreachable!()
};
let rigid = unifier.get_fresh_rigid_var(*name, *loc).ty;
no_ranges.push(rigid);
vec![rigid]
})
}) })
.multi_cartesian_product() })
.collect_vec(); .multi_cartesian_product()
let mut result: Vec<VarMap> = Vec::default(); .collect_vec();
for comb in var_combs { let mut result: Vec<VarMap> = Vec::default();
result.push(vars.keys().copied().zip(comb).collect()); for comb in var_combs {
result.push(vars.keys().copied().zip(comb).collect());
}
// NOTE: if is empty, means no type var, append a empty subst, ok to do this?
if result.is_empty() {
result.push(VarMap::new());
}
(result, no_ranges)
};
for subst in type_var_subst_comb {
// for each instance
let inst_ret = unifier.subst(*ret, &subst).unwrap_or(*ret);
let inst_args = {
args.iter()
.map(|a| FuncArg {
name: a.name,
ty: unifier.subst(a.ty, &subst).unwrap_or(a.ty),
default_value: a.default_value.clone(),
is_vararg: false,
})
.collect_vec()
};
let self_type = {
uninst_self_type.clone().map(|(self_type, type_vars)| {
let subst_for_self = {
let class_ty_var_ids = type_vars
.iter()
.map(|x| {
if let TypeEnum::TVar { id, .. } = &*unifier.get_ty(*x) {
*id
} else {
unreachable!("must be type var here");
}
})
.collect::<HashSet<_>>();
subst
.iter()
.filter_map(|(ty_var_id, ty_var_target)| {
if class_ty_var_ids.contains(ty_var_id) {
Some((*ty_var_id, *ty_var_target))
} else {
None
}
})
.collect::<VarMap>()
};
unifier.subst(self_type, &subst_for_self).unwrap_or(self_type)
})
};
let mut identifiers = {
let mut result = HashMap::new();
if self_type.is_some() {
result.insert("self".into(), IdentifierInfo::default());
} }
// NOTE: if is empty, means no type var, append a empty subst, ok to do this? result.extend(inst_args.iter().map(|x| (x.name, IdentifierInfo::default())));
if result.is_empty() { result
result.push(VarMap::new()); };
} let mut calls: HashMap<CodeLocation, CallId> = HashMap::new();
(result, no_ranges) let mut inferencer = Inferencer {
top_level: ctx.as_ref(),
defined_identifiers: identifiers.clone(),
function_data: &mut FunctionData {
resolver: resolver.as_ref().unwrap().clone(),
return_type: if unifier.unioned(inst_ret, primitives_ty.none) {
None
} else {
Some(inst_ret)
},
// NOTE: allowed type vars
bound_variables: no_range_vars.clone(),
},
unifier,
variable_mapping: {
let mut result: HashMap<StrRef, Type> = HashMap::new();
if let Some(self_ty) = self_type {
result.insert("self".into(), self_ty);
}
result.extend(inst_args.iter().map(|x| (x.name, x.ty)));
result
},
primitives: primitives_ty,
virtual_checks: &mut Vec::new(),
calls: &mut calls,
in_handler: false,
}; };
for subst in type_var_subst_comb { let ast::StmtKind::FunctionDef { body, decorator_list, .. } =
// for each instance ast.clone().unwrap().node
let inst_ret = unifier.subst(*ret, &subst).unwrap_or(*ret); else {
let inst_args = { unreachable!("must be function def ast")
args.iter() };
.map(|a| FuncArg {
name: a.name, if !decorator_list.is_empty() {
ty: unifier.subst(a.ty, &subst).unwrap_or(a.ty), if matches!(&decorator_list[0].node, ExprKind::Name { id, .. } if id == &"extern".into())
default_value: a.default_value.clone(), {
is_vararg: false, let TopLevelDef::Function { instance_to_symbol, .. } = &mut *def.write()
}) else {
.collect_vec() unreachable!()
}; };
let self_type = { instance_to_symbol.insert(String::new(), simple_name.to_string());
uninst_self_type.clone().map(|(self_type, type_vars)| { continue;
let subst_for_self = { }
let class_ty_var_ids = type_vars
.iter() if matches!(&decorator_list[0].node, ExprKind::Name { id, .. } if id == &"rpc".into())
.map(|x| { {
if let TypeEnum::TVar { id, .. } = &*unifier.get_ty(*x) { let TopLevelDef::Function { instance_to_symbol, .. } = &mut *def.write()
*id else {
} else { unreachable!()
unreachable!("must be type var here"); };
} instance_to_symbol.insert(String::new(), simple_name.to_string());
}) continue;
.collect::<HashSet<_>>(); }
subst
.iter() if let ExprKind::Call { func, .. } = &decorator_list[0].node {
.filter_map(|(ty_var_id, ty_var_target)| { if matches!(&func.node, ExprKind::Name { id, .. } if id == &"rpc".into()) {
if class_ty_var_ids.contains(ty_var_id) { let TopLevelDef::Function { instance_to_symbol, .. } =
Some((*ty_var_id, *ty_var_target)) &mut *def.write()
} else { else {
None unreachable!()
}
})
.collect::<VarMap>()
}; };
unifier.subst(self_type, &subst_for_self).unwrap_or(self_type) instance_to_symbol.insert(String::new(), simple_name.to_string());
}) continue;
};
let mut identifiers = {
let mut result = HashMap::new();
if self_type.is_some() {
result.insert("self".into(), IdentifierInfo::default());
}
result
.extend(inst_args.iter().map(|x| (x.name, IdentifierInfo::default())));
result
};
let mut calls: HashMap<CodeLocation, CallId> = HashMap::new();
let mut inferencer = Inferencer {
top_level: ctx.as_ref(),
defined_identifiers: identifiers.clone(),
function_data: &mut FunctionData {
resolver: resolver.as_ref().unwrap().clone(),
return_type: if unifier.unioned(inst_ret, primitives_ty.none) {
None
} else {
Some(inst_ret)
},
// NOTE: allowed type vars
bound_variables: no_range_vars.clone(),
},
unifier,
variable_mapping: {
let mut result: HashMap<StrRef, Type> = HashMap::new();
if let Some(self_ty) = self_type {
result.insert("self".into(), self_ty);
}
result.extend(inst_args.iter().map(|x| (x.name, x.ty)));
result
},
primitives: primitives_ty,
virtual_checks: &mut Vec::new(),
calls: &mut calls,
in_handler: false,
};
let ast::StmtKind::FunctionDef { body, decorator_list, .. } =
ast.clone().unwrap().node
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())
{
instance_to_symbol.insert(String::new(), simple_name.to_string());
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<_>, _>>()?;
let returned = inferencer.check_block(fun_body.as_slice(), &mut identifiers)?; let returned = inferencer.check_block(fun_body.as_slice(), &mut identifiers)?;
{ {
// check virtuals // check virtuals
let defs = ctx.definitions.read(); let defs = ctx.definitions.read();
for (subtype, base, loc) in &*inferencer.virtual_checks { for (subtype, base, loc) in &*inferencer.virtual_checks {
let base_id = { let base_id = {
let base = inferencer.unifier.get_ty(*base); let base = inferencer.unifier.get_ty(*base);
if let TypeEnum::TObj { obj_id, .. } = &*base { if let TypeEnum::TObj { obj_id, .. } = &*base {
*obj_id *obj_id
} else { } else {
return Err(HashSet::from([format!( return Err(HashSet::from([format!(
"Base type should be a class (at {loc})" "Base type should be a class (at {loc})"
)])); )]));
} }
}; };
let subtype_id = { let subtype_id = {
let ty = inferencer.unifier.get_ty(*subtype); let ty = inferencer.unifier.get_ty(*subtype);
if let TypeEnum::TObj { obj_id, .. } = &*ty { if let TypeEnum::TObj { obj_id, .. } = &*ty {
*obj_id *obj_id
} else { } else {
let base_repr = inferencer.unifier.stringify(*base);
let subtype_repr = inferencer.unifier.stringify(*subtype);
return Err(HashSet::from([format!(
"Expected a subtype of {base_repr}, but got {subtype_repr} (at {loc})"),
]));
}
};
let subtype_entry = defs[subtype_id.0].read();
let TopLevelDef::Class { ancestors, .. } = &*subtype_entry else {
unreachable!()
};
let m = ancestors.iter()
.find(|kind| matches!(kind, TypeAnnotation::CustomClass { id, .. } if *id == base_id));
if m.is_none() {
let base_repr = inferencer.unifier.stringify(*base); let base_repr = inferencer.unifier.stringify(*base);
let subtype_repr = inferencer.unifier.stringify(*subtype); let subtype_repr = inferencer.unifier.stringify(*subtype);
return Err(HashSet::from([format!( return Err(HashSet::from([format!(
"Expected a subtype of {base_repr}, but got {subtype_repr} (at {loc})"), "Expected a subtype of {base_repr}, but got {subtype_repr} (at {loc})"),
])); ]));
} }
};
let subtype_entry = defs[subtype_id.0].read();
let TopLevelDef::Class { ancestors, .. } = &*subtype_entry else {
unreachable!()
};
let m = ancestors.iter()
.find(|kind| matches!(kind, TypeAnnotation::CustomClass { id, .. } if *id == base_id));
if m.is_none() {
let base_repr = inferencer.unifier.stringify(*base);
let subtype_repr = inferencer.unifier.stringify(*subtype);
return Err(HashSet::from([format!(
"Expected a subtype of {base_repr}, but got {subtype_repr} (at {loc})"),
]));
} }
} }
if !unifier.unioned(inst_ret, primitives_ty.none) && !returned {
let def_ast_list = &definition_ast_list;
let ret_str = unifier.internal_stringify(
inst_ret,
&mut |id| {
let TopLevelDef::Class { name, .. } = &*def_ast_list[id].0.read()
else {
unreachable!("must be class id here")
};
name.to_string()
},
&mut |id| format!("typevar{id}"),
&mut None,
);
return Err(HashSet::from([format!(
"expected return type of `{}` in function `{}` (at {})",
ret_str,
name,
ast.as_ref().unwrap().location
)]));
}
instance_to_stmt.insert(
get_subst_key(
unifier,
self_type,
&subst,
Some(&vars.keys().copied().collect()),
),
FunInstance {
body: Arc::new(fun_body),
unifier_id: 0,
calls: Arc::new(calls),
subst,
},
);
} }
if !unifier.unioned(inst_ret, primitives_ty.none) && !returned {
let def_ast_list = &definition_ast_list;
let ret_str = unifier.internal_stringify(
inst_ret,
&mut |id| {
let TopLevelDef::Class { name, .. } = &*def_ast_list[id].0.read()
else {
unreachable!("must be class id here")
};
name.to_string()
},
&mut |id| format!("typevar{id}"),
&mut None,
);
return Err(HashSet::from([format!(
"expected return type of `{}` in function `{}` (at {})",
ret_str,
name,
ast.as_ref().unwrap().location
)]));
}
let TopLevelDef::Function { instance_to_stmt, .. } = &mut *def.write() else {
unreachable!()
};
instance_to_stmt.insert(
get_subst_key(
unifier,
self_type,
&subst,
Some(&vars.keys().copied().collect()),
),
FunInstance {
body: Arc::new(fun_body),
unifier_id: 0,
calls: Arc::new(calls),
subst,
},
);
} }
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;

View File

@ -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) = self.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!(

View File

@ -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)
}
} }