Skip to content

Commit

Permalink
Initial simplification of type guards
Browse files Browse the repository at this point in the history
  • Loading branch information
Rigidity committed Aug 3, 2024
1 parent 11c220d commit 9557f08
Show file tree
Hide file tree
Showing 10 changed files with 219 additions and 238 deletions.
30 changes: 11 additions & 19 deletions crates/rue-compiler/src/compiler.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#![allow(clippy::map_unwrap_or)]

use rue_typing::HashMap;
use rue_typing::{HashMap, TypePath};

pub(crate) use builtins::Builtins;

Expand All @@ -12,7 +12,7 @@ use crate::{
database::{Database, HirId, ScopeId, SymbolId},
hir::{Hir, Op},
scope::Scope,
value::{GuardPath, Mutation, TypeOverride, Value},
value::{GuardPath, Value},
ErrorKind,
};

Expand Down Expand Up @@ -47,7 +47,7 @@ pub struct Compiler<'a> {
type_definition_stack: Vec<TypeId>,

// The type guard stack is used for overriding types in certain contexts.
type_guard_stack: Vec<HashMap<GuardPath, TypeOverride>>,
type_guard_stack: Vec<HashMap<GuardPath, TypeId>>,

// The generic type stack is used for overriding generic types that are being checked against.
generic_type_stack: Vec<HashMap<TypeId, TypeId>>,
Expand Down Expand Up @@ -88,15 +88,14 @@ impl<'a> Compiler<'a> {
self.sym
}

fn compile_index(&mut self, value: HirId, index: usize, rest: bool) -> HirId {
let mut result = value;
for _ in 0..index {
result = self.db.alloc_hir(Hir::Op(Op::Rest, result));
}
if !rest {
result = self.db.alloc_hir(Hir::Op(Op::First, result));
fn hir_path(&mut self, mut value: HirId, path_items: &[TypePath]) -> HirId {
for path in path_items {
match path {
TypePath::First => value = self.db.alloc_hir(Hir::Op(Op::First, value)),
TypePath::Rest => value = self.db.alloc_hir(Hir::Op(Op::Rest, value)),
}
}
result
value
}

fn type_reference(&mut self, referenced_type_id: TypeId) {
Expand Down Expand Up @@ -170,7 +169,7 @@ impl<'a> Compiler<'a> {
Value::new(self.builtins.unknown, self.ty.std().unknown)
}

fn symbol_type(&self, guard_path: &GuardPath) -> Option<TypeOverride> {
fn symbol_type(&self, guard_path: &GuardPath) -> Option<TypeId> {
for guards in self.type_guard_stack.iter().rev() {
if let Some(guard) = guards.get(guard_path) {
return Some(*guard);
Expand All @@ -179,13 +178,6 @@ impl<'a> Compiler<'a> {
None
}

fn apply_mutation(&mut self, hir_id: HirId, mutation: Mutation) -> HirId {
match mutation {
Mutation::None => hir_id,
Mutation::UnwrapOptional => self.db.alloc_hir(Hir::Op(Op::First, hir_id)),
}
}

fn scope(&self) -> &Scope {
self.db
.scope(self.scope_stack.last().copied().expect("no scope found"))
Expand Down
16 changes: 7 additions & 9 deletions crates/rue-compiler/src/compiler/expr/binary_expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use rue_typing::{Comparison, Type, TypeId};
use crate::{
compiler::Compiler,
hir::{BinOp, Hir, Op},
value::{Guard, TypeOverride, Value},
value::{Guard, Value},
ErrorKind, HirId,
};

Expand Down Expand Up @@ -170,21 +170,19 @@ impl Compiler<'_> {
if let Some(guard_path) = rhs.guard_path {
let then_type = self.ty.std().nil;
let else_type = self.ty.difference(rhs.type_id, self.ty.std().nil);
value.guards.insert(
guard_path,
Guard::new(TypeOverride::new(then_type), TypeOverride::new(else_type)),
);
value
.guards
.insert(guard_path, Guard::new(then_type, else_type));
}
}

if self.ty.compare(rhs.type_id, self.ty.std().nil) == Comparison::Equal {
if let Some(guard_path) = lhs.guard_path.clone() {
let then_type = self.ty.std().nil;
let else_type = self.ty.difference(lhs.type_id, self.ty.std().nil);
value.guards.insert(
guard_path,
Guard::new(TypeOverride::new(then_type), TypeOverride::new(else_type)),
);
value
.guards
.insert(guard_path, Guard::new(then_type, else_type));
}
}

Expand Down
237 changes: 137 additions & 100 deletions crates/rue-compiler/src/compiler/expr/field_access_expr.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
use rue_parser::FieldAccessExpr;
use rue_typing::{deconstruct_items, Type};
use rue_parser::{FieldAccessExpr, SyntaxToken};
use rue_typing::{deconstruct_items, index_to_path, Struct, Type, TypeId, TypePath, Variant};

use crate::{
compiler::Compiler,
hir::{Hir, Op},
value::{GuardPathItem, Value},
value::Value,
ErrorKind,
};

Expand All @@ -18,132 +18,169 @@ impl Compiler<'_> {
return self.unknown();
};

let Some(field_name) = field_access.field() else {
let Some(name) = field_access.field() else {
return self.unknown();
};

let mut new_value = match self.ty.get(old_value.type_id).clone() {
Type::Unknown => return self.unknown(),
Type::Struct(ty) => {
let fields =
deconstruct_items(self.ty, ty.type_id, ty.field_names.len(), ty.nil_terminated)
.expect("invalid struct type");

if let Some(index) = ty.field_names.get_index_of(field_name.text()) {
let type_id = fields[index];

Value::new(
self.compile_index(
old_value.hir_id,
index,
index == ty.field_names.len() - 1 && !ty.nil_terminated,
),
type_id,
)
.extend_guard_path(old_value, GuardPathItem::Field(field_name.to_string()))
} else {
self.db.error(
ErrorKind::UnknownField(field_name.to_string()),
field_name.text_range(),
);
let Some(value) = self.compile_struct_field_access(old_value, &ty, &name) else {
return self.unknown();
}
}
Type::Variant(variant) => {
let field_names = variant.field_names.clone().unwrap_or_default();

let Type::Enum(ty) = self.ty.get(variant.original_enum_type_id) else {
unreachable!();
};

let fields = if ty.has_fields {
let type_id = self
.ty
.get_pair(variant.type_id)
.expect("expected a pair")
.1;

variant
.field_names
.as_ref()
.map(|field_names| {
deconstruct_items(
self.ty,
type_id,
field_names.len(),
variant.nil_terminated,
)
.expect("invalid struct type")
})
.unwrap_or_default()
} else {
Vec::new()
};

if let Some(index) = field_names.get_index_of(field_name.text()) {
let type_id = fields[index];

let fields_hir_id = self.db.alloc_hir(Hir::Op(Op::Rest, old_value.hir_id));

Value::new(
self.compile_index(
fields_hir_id,
index,
index == fields.len() - 1 && !variant.nil_terminated,
),
type_id,
)
.extend_guard_path(old_value, GuardPathItem::Field(field_name.to_string()))
} else {
self.db.error(
ErrorKind::UnknownField(field_name.to_string()),
field_name.text_range(),
);
value
}
Type::Variant(ty) => {
let Some(value) = self.compile_variant_field_access(old_value, &ty, &name) else {
return self.unknown();
}
};
value
}
Type::Pair(first, rest) => match field_name.text() {
"first" => Value::new(
self.db.alloc_hir(Hir::Op(Op::First, old_value.hir_id)),
first,
)
.extend_guard_path(old_value, GuardPathItem::First),
"rest" => Value::new(self.db.alloc_hir(Hir::Op(Op::Rest, old_value.hir_id)), rest)
.extend_guard_path(old_value, GuardPathItem::Rest),
_ => {
self.db.error(
ErrorKind::InvalidFieldAccess(
field_name.to_string(),
self.type_name(old_value.type_id),
),
field_name.text_range(),
);
Type::Pair(first, rest) => {
let Some(value) = self.compile_pair_field_access(old_value, first, rest, &name)
else {
return self.unknown();
}
},
Type::Bytes | Type::Bytes32 if field_name.text() == "length" => Value::new(
};
value
}
Type::Bytes | Type::Bytes32 if name.text() == "length" => Value::new(
self.db.alloc_hir(Hir::Op(Op::Strlen, old_value.hir_id)),
self.ty.std().int,
),
_ => {
self.db.error(
ErrorKind::InvalidFieldAccess(
field_name.to_string(),
name.to_string(),
self.type_name(old_value.type_id),
),
field_name.text_range(),
name.text_range(),
);
return self.unknown();
}
};

if let Some(guard_path) = new_value.guard_path.as_ref() {
if let Some(type_override) = self.symbol_type(guard_path) {
new_value.type_id = type_override.type_id;
new_value.hir_id = self.apply_mutation(new_value.hir_id, type_override.mutation);
new_value.type_id = type_override;
}
}

new_value
}

fn compile_pair_field_access(
&mut self,
old_value: Value,
first: TypeId,
rest: TypeId,
name: &SyntaxToken,
) -> Option<Value> {
let path = match name.text() {
"first" => TypePath::First,
"rest" => TypePath::Rest,
_ => {
self.db.error(
ErrorKind::InvalidFieldAccess(
name.to_string(),
self.type_name(old_value.type_id),
),
name.text_range(),
);
return None;
}
};

let type_id = match path {
TypePath::First => first,
TypePath::Rest => rest,
};

let mut value = Value::new(self.hir_path(old_value.hir_id, &[path]), type_id);

value.guard_path = old_value.guard_path.map(|mut guard_path| {
guard_path.items.push(path);
guard_path
});

Some(value)
}

fn compile_struct_field_access(
&mut self,
old_value: Value,
ty: &Struct,
name: &SyntaxToken,
) -> Option<Value> {
let fields =
deconstruct_items(self.ty, ty.type_id, ty.field_names.len(), ty.nil_terminated)
.expect("invalid struct type");

let Some(index) = ty.field_names.get_index_of(name.text()) else {
self.db
.error(ErrorKind::UnknownField(name.to_string()), name.text_range());
return None;
};

let type_id = fields[index];

let path_items = index_to_path(
index,
index != ty.field_names.len() - 1 || ty.nil_terminated,
);

let mut value = Value::new(self.hir_path(old_value.hir_id, &path_items), type_id);

value.guard_path = old_value.guard_path.map(|mut guard_path| {
guard_path.items.extend(path_items);
guard_path
});

Some(value)
}

fn compile_variant_field_access(
&mut self,
old_value: Value,
ty: &Variant,
name: &SyntaxToken,
) -> Option<Value> {
let field_names = ty.field_names.clone().unwrap_or_default();

let Type::Enum(enum_type) = self.ty.get(ty.original_enum_type_id) else {
unreachable!();
};

let fields = if enum_type.has_fields {
let type_id = self.ty.get_pair(ty.type_id).expect("expected a pair").1;

ty.field_names
.as_ref()
.map(|field_names| {
deconstruct_items(self.ty, type_id, field_names.len(), ty.nil_terminated)
.expect("invalid struct type")
})
.unwrap_or_default()
} else {
Vec::new()
};

let Some(index) = field_names.get_index_of(name.text()) else {
self.db
.error(ErrorKind::UnknownField(name.to_string()), name.text_range());
return None;
};

let type_id = fields[index];

let path_items = index_to_path(index + 1, index != fields.len() - 1 || ty.nil_terminated);

let mut value = Value::new(self.hir_path(old_value.hir_id, &path_items), type_id);

value.guard_path = old_value.guard_path.map(|mut guard_path| {
guard_path.items.extend(path_items);
guard_path
});

Some(value)
}
}
Loading

0 comments on commit 9557f08

Please sign in to comment.