Compare commits

...

4 Commits

3 changed files with 103 additions and 11 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

@ -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,7 @@ impl<'a> Inferencer<'a> {
*id, *id,
) { ) {
Ok(_) => { Ok(_) => {
self.defined_identifiers.insert(*id, IdentifierInfo::default()); defined_identifiers.insert(*id, IdentifierInfo::default());
} }
Err(e) => { Err(e) => {
return Err(HashSet::from([format!( return Err(HashSet::from([format!(
@ -368,9 +382,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 used prior to global declaration at {}",
stmt.location, stmt.location,
)])); )]));
} }
@ -385,8 +399,12 @@ impl<'a> Inferencer<'a> {
*id, *id,
) { ) {
Ok(_) => { Ok(_) => {
self.defined_identifiers self.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

@ -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 {
@ -566,15 +586,45 @@ impl<'a> Fold<()> for Inferencer<'a> {
unreachable!("must be tobj") unreachable!("must be tobj")
} }
} else { } else {
let top_level_defs = &self.top_level.definitions.read();
if !self.defined_identifiers.contains_key(id) { if !self.defined_identifiers.contains_key(id) {
match self.function_data.resolver.get_symbol_type( match self.function_data.resolver.get_symbol_type(
self.unifier, self.unifier,
&self.top_level.definitions.read(), top_level_defs,
self.primitives, self.primitives,
*id, *id,
) { ) {
Ok(_) => { Ok(_) => {
self.defined_identifiers.insert(*id, IdentifierInfo::default()); // Determine if the referenced id refers to a global symbol
let is_explicit = top_level_defs
.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);
self.defined_identifiers.insert(
*id,
IdentifierInfo {
source: match is_explicit {
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(