Compare commits
4 Commits
master
...
enhance/gl
Author | SHA1 | Date |
---|---|---|
David Mak | 3afe69fcda | |
David Mak | b538611cb0 | |
David Mak | fa573390b1 | |
David Mak | 3ca70e679a |
|
@ -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, .. } => {
|
||||
|
|
|
@ -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) = 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) {
|
||||
defined_identifiers.insert(*id, IdentifierInfo::default());
|
||||
}
|
||||
|
@ -104,7 +118,7 @@ impl<'a> Inferencer<'a> {
|
|||
*id,
|
||||
) {
|
||||
Ok(_) => {
|
||||
self.defined_identifiers.insert(*id, IdentifierInfo::default());
|
||||
defined_identifiers.insert(*id, IdentifierInfo::default());
|
||||
}
|
||||
Err(e) => {
|
||||
return Err(HashSet::from([format!(
|
||||
|
@ -368,9 +382,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 used prior to global declaration at {}",
|
||||
stmt.location,
|
||||
)]));
|
||||
}
|
||||
|
@ -385,8 +399,12 @@ impl<'a> Inferencer<'a> {
|
|||
*id,
|
||||
) {
|
||||
Ok(_) => {
|
||||
self.defined_identifiers
|
||||
.insert(*id, IdentifierInfo { is_global: true });
|
||||
self.defined_identifiers.insert(
|
||||
*id,
|
||||
IdentifierInfo {
|
||||
source: DeclarationSource::Global { is_explicit: Some(true) },
|
||||
},
|
||||
);
|
||||
}
|
||||
Err(e) => {
|
||||
return Err(HashSet::from([format!(
|
||||
|
|
|
@ -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 {
|
||||
|
@ -566,15 +586,45 @@ impl<'a> Fold<()> for Inferencer<'a> {
|
|||
unreachable!("must be tobj")
|
||||
}
|
||||
} else {
|
||||
let top_level_defs = &self.top_level.definitions.read();
|
||||
|
||||
if !self.defined_identifiers.contains_key(id) {
|
||||
match self.function_data.resolver.get_symbol_type(
|
||||
self.unifier,
|
||||
&self.top_level.definitions.read(),
|
||||
top_level_defs,
|
||||
self.primitives,
|
||||
*id,
|
||||
) {
|
||||
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) => {
|
||||
return report_error(
|
||||
|
|
Loading…
Reference in New Issue