diff options
author | Matthias Schiffer <mschiffer@universe-factory.net> | 2024-05-01 22:01:30 +0200 |
---|---|---|
committer | Matthias Schiffer <mschiffer@universe-factory.net> | 2024-05-01 22:18:02 +0200 |
commit | 537be83c9faa105a73465fd18227de1e54859403 (patch) | |
tree | 687b25d621f6e8e60bfc7fcbdf2997bdab6b1c72 /crates | |
parent | 0d980906852c9a22120c1b7992d2cc0661a63a80 (diff) | |
download | rebel-537be83c9faa105a73465fd18227de1e54859403.tar rebel-537be83c9faa105a73465fd18227de1e54859403.zip |
rebel-lang: scope: store initialization status separately from value
By storing a Value instead of an Option<Value>, we can just pass around
a &mut Value instead of a ValueSlot. The initialized flag can also be
used for typechecking, when no Value is available.
Diffstat (limited to 'crates')
-rw-r--r-- | crates/rebel-lang/benches/recipe.rs | 7 | ||||
-rw-r--r-- | crates/rebel-lang/src/scope.rs | 134 | ||||
-rw-r--r-- | crates/rebel-lang/src/typing.rs | 7 | ||||
-rw-r--r-- | crates/rebel-lang/src/value.rs | 12 |
4 files changed, 86 insertions, 74 deletions
diff --git a/crates/rebel-lang/benches/recipe.rs b/crates/rebel-lang/benches/recipe.rs index 3506416..6ee1697 100644 --- a/crates/rebel-lang/benches/recipe.rs +++ b/crates/rebel-lang/benches/recipe.rs @@ -1,6 +1,7 @@ use rebel_lang::{ scope::{Context, ModuleEntry, Var}, typing::Type, + value::Value, }; use rebel_parse::ast; @@ -23,7 +24,8 @@ fn context() -> Context { ModuleEntry::Def(Var { explicit_type: Type::Str, inferred_type: Type::Str, - value: None, + value: Value::Uninitialized, + initialized: true, }), ); ctx.vars.0.insert( @@ -31,7 +33,8 @@ fn context() -> Context { ModuleEntry::Def(Var { explicit_type: Type::Str, inferred_type: Type::Str, - value: None, + value: Value::Uninitialized, + initialized: true, }), ); diff --git a/crates/rebel-lang/src/scope.rs b/crates/rebel-lang/src/scope.rs index b8e31a6..f0fe443 100644 --- a/crates/rebel-lang/src/scope.rs +++ b/crates/rebel-lang/src/scope.rs @@ -13,33 +13,8 @@ use crate::{ pub struct Var { pub explicit_type: Type, pub inferred_type: Type, - pub value: Option<Value>, -} - -#[derive(Debug)] -enum ValueSlot<'a> { - Value(&'a mut Value), - Option(&'a mut Option<Value>), -} - -impl<'a> ValueSlot<'a> { - fn assign(&mut self, value: Value) { - match self { - ValueSlot::Value(slot) => { - **slot = value; - } - ValueSlot::Option(slot) => { - **slot = Some(value); - } - } - } - - fn into_inner(self) -> Option<&'a mut Value> { - match self { - ValueSlot::Value(slot) => Some(slot), - ValueSlot::Option(slot) => slot.as_mut(), - } - } + pub value: Value, + pub initialized: bool, } #[derive(Debug, Clone)] @@ -114,44 +89,66 @@ impl Context { .ok_or(Error::lookup("undefined variable")) } - fn lookup_destr_pat_var_type_mut(&mut self, pat: &pat::DestrPat) -> Result<&mut Type> { + fn lookup_destr_pat_var_type_mut( + &mut self, + pat: &pat::DestrPat, + ) -> Result<(&mut Type, &mut bool)> { Ok(match pat { pat::DestrPat::Index { base, index } => { - // TODO: Catch uninitialized base match Type::ast_expr_type(self, index)? { Type::Int => {} _ => { return Err(Error::typ("invalid array index type")); } } - match self.lookup_destr_pat_var_type_mut(base)? { - Type::Array(inner) => inner.as_mut(), - _ => return Err(Error::typ("invalid array index base")), + let (typ, initialized) = self.lookup_destr_pat_var_type_mut(base)?; + if !*initialized { + return Err(Error::typ( + "tried to assign field of uninitialized variable", + )); } + ( + match typ { + Type::Array(inner) => inner.as_mut(), + _ => return Err(Error::typ("invalid array index base")), + }, + initialized, + ) } pat::DestrPat::Field { base, field } => { - // TODO: Catch uninitialized base - match self.lookup_destr_pat_var_type_mut(base)? { - Type::Tuple(elems) => { - let index: usize = - field.name.parse().or(Err(Error::typ("no such field")))?; - elems.get_mut(index).ok_or(Error::typ("no such field"))? - } - Type::Struct(entries) => entries - .get_mut(field.name) - .ok_or(Error::typ("no such field"))?, - _ => return Err(Error::typ("invalid field access base type")), + let (typ, initialized) = self.lookup_destr_pat_var_type_mut(base)?; + if !*initialized { + return Err(Error::typ( + "tried to assign field of uninitialized variable", + )); } + ( + match typ { + Type::Tuple(elems) => { + let index: usize = + field.name.parse().or(Err(Error::typ("no such field")))?; + elems.get_mut(index).ok_or(Error::typ("no such field"))? + } + Type::Struct(entries) => entries + .get_mut(field.name) + .ok_or(Error::typ("no such field"))?, + _ => return Err(Error::typ("invalid field access base type")), + }, + initialized, + ) } pat::DestrPat::Paren(subpat) => self.lookup_destr_pat_var_type_mut(subpat)?, - pat::DestrPat::Path(path) => &mut self.lookup_path_var_mut(path)?.inferred_type, + pat::DestrPat::Path(path) => { + let var = self.lookup_path_var_mut(path)?; + (&mut var.inferred_type, &mut var.initialized) + } }) } fn lookup_destr_pat_var_type_value_mut( &mut self, pat: &pat::DestrPat, - ) -> Result<(&mut Type, ValueSlot)> { + ) -> Result<(&mut Type, &mut Value, &mut bool)> { Ok(match pat { pat::DestrPat::Index { base, index } => { let index = usize::try_from(match Value::eval(self, index)? { @@ -161,13 +158,13 @@ impl Context { } }) .or(Err(Error::eval("array index out of bounds")))?; - let (typ, value) = self.lookup_destr_pat_var_type_value_mut(base)?; - let (inner_type, inner_value) = match ( - typ, - value.into_inner().ok_or(Error::typ( + let (typ, value, initialized) = self.lookup_destr_pat_var_type_value_mut(base)?; + if !*initialized { + return Err(Error::typ( "tried to assign field of uninitialized variable", - ))?, - ) { + )); + } + let (inner_type, inner_value) = match (typ, value) { (Type::Array(inner_type), Value::Array(inner_value)) => ( inner_type.as_mut(), inner_value @@ -176,16 +173,16 @@ impl Context { ), _ => return Err(Error::typ("invalid array index base")), }; - (inner_type, ValueSlot::Value(inner_value)) + (inner_type, inner_value, initialized) } pat::DestrPat::Field { base, field } => { - let (typ, value) = self.lookup_destr_pat_var_type_value_mut(base)?; - let (inner_type, inner_value) = match ( - typ, - value - .into_inner() - .ok_or(Error::typ("tried to assign field of unitialized variable"))?, - ) { + let (typ, value, initialized) = self.lookup_destr_pat_var_type_value_mut(base)?; + if !*initialized { + return Err(Error::typ( + "tried to assign field of uninitialized variable", + )); + } + let (inner_type, inner_value) = match (typ, value) { (Type::Tuple(type_elems), Value::Tuple(value_elems)) => { let index: usize = field.name.parse().or(Err(Error::typ("no such field")))?; @@ -208,12 +205,12 @@ impl Context { ), _ => return Err(Error::typ("invalid field access base type")), }; - (inner_type, ValueSlot::Value(inner_value)) + (inner_type, inner_value, initialized) } pat::DestrPat::Paren(subpat) => self.lookup_destr_pat_var_type_value_mut(subpat)?, pat::DestrPat::Path(path) => { let var = self.lookup_path_var_mut(path)?; - (&mut var.inferred_type, ValueSlot::Option(&mut var.value)) + (&mut var.inferred_type, &mut var.value, &mut var.initialized) } }) } @@ -235,10 +232,11 @@ impl Context { return Ok(Type::Unit); } - let dest_type = self.lookup_destr_pat_var_type_mut(dest)?; + let (dest_type, initialized) = self.lookup_destr_pat_var_type_mut(dest)?; let inferred_type = dest_type.clone().unify(typ, Coerce::Common)?; *dest_type = inferred_type.clone(); + *initialized = true; // TODO: Check explicit type @@ -252,10 +250,12 @@ impl Context { return Ok(Value::Unit); } - let (dest_type, mut dest_value) = self.lookup_destr_pat_var_type_value_mut(dest)?; + let (dest_type, dest_value, initialized) = + self.lookup_destr_pat_var_type_value_mut(dest)?; *dest_type = dest_type.clone().unify(typ, Coerce::Common)?; - dest_value.assign(value.clone()); + *dest_value = value.clone(); + *initialized = true; // TODO: Check explicit type @@ -295,7 +295,8 @@ impl Context { Var { inferred_type: explicit_type.clone(), explicit_type, - value: None, + value: Value::Uninitialized, + initialized: false, }, ); @@ -341,7 +342,8 @@ impl Context { Var { inferred_type: explicit_type.clone(), explicit_type, - value: None, + value: Value::Uninitialized, + initialized: false, }, ); diff --git a/crates/rebel-lang/src/typing.rs b/crates/rebel-lang/src/typing.rs index 034bcd4..8edc36a 100644 --- a/crates/rebel-lang/src/typing.rs +++ b/crates/rebel-lang/src/typing.rs @@ -296,8 +296,11 @@ impl Type { } fn path_expr_type(ctx: &Context, path: &ast::Path<'_>) -> Result<Type> { - // TODO: Catch uninitialized field access - Ok(ctx.lookup_var(path)?.inferred_type.clone()) + let var = ctx.lookup_var(path)?; + if !var.initialized { + return Err(Error::typ("uninitialized variable")); + } + Ok(var.inferred_type.clone()) } fn check_string_piece( diff --git a/crates/rebel-lang/src/value.rs b/crates/rebel-lang/src/value.rs index 33af086..bb8556d 100644 --- a/crates/rebel-lang/src/value.rs +++ b/crates/rebel-lang/src/value.rs @@ -15,6 +15,7 @@ use crate::{ #[derive(Debug, Clone, PartialEq, Eq)] pub enum Value { + Uninitialized, Unit, Bool(bool), Int(i64), @@ -28,6 +29,7 @@ pub enum Value { impl Value { pub fn typ(&self) -> Result<Type> { Ok(match self { + Value::Uninitialized => return Err(Error::typ("uninitialized value")), Value::Unit => Type::Unit, Value::Bool(_) => Type::Bool, Value::Int(_) => Type::Int, @@ -264,10 +266,11 @@ impl Value { } fn eval_path(ctx: &Context, path: &ast::Path<'_>) -> Result<Value> { - ctx.lookup_var(path)? - .value - .clone() - .ok_or(Error::typ("uninitialized variable")) + let var = ctx.lookup_var(path)?; + if !var.initialized { + return Err(Error::typ("uninitialized variable")); + } + Ok(var.value.clone()) } fn eval_literal(ctx: &Context, lit: &expr::Literal<'_>) -> Result<Value> { @@ -311,6 +314,7 @@ impl Value { impl Display for Value { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { + Value::Uninitialized => f.write_str("_"), Value::Unit => f.write_str("()"), Value::Bool(value) => value.fmt(f), Value::Int(value) => value.fmt(f), |