summaryrefslogtreecommitdiffstats
path: root/crates
diff options
context:
space:
mode:
authorMatthias Schiffer <mschiffer@universe-factory.net>2024-05-01 22:01:30 +0200
committerMatthias Schiffer <mschiffer@universe-factory.net>2024-05-01 22:18:02 +0200
commit537be83c9faa105a73465fd18227de1e54859403 (patch)
tree687b25d621f6e8e60bfc7fcbdf2997bdab6b1c72 /crates
parent0d980906852c9a22120c1b7992d2cc0661a63a80 (diff)
downloadrebel-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.rs7
-rw-r--r--crates/rebel-lang/src/scope.rs134
-rw-r--r--crates/rebel-lang/src/typing.rs7
-rw-r--r--crates/rebel-lang/src/value.rs12
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),