Skip to content

Commit

Permalink
Improve optional types
Browse files Browse the repository at this point in the history
  • Loading branch information
Rigidity committed Jul 4, 2024
1 parent 890d241 commit e0d5e6c
Show file tree
Hide file tree
Showing 25 changed files with 214 additions and 130 deletions.
19 changes: 13 additions & 6 deletions crates/rue-compiler/src/compiler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use crate::{
database::{Database, HirId, ScopeId, SymbolId, TypeId},
hir::{Hir, Op},
scope::Scope,
value::{GuardPath, PairType, Type, Value},
value::{GuardPath, Mutation, PairType, Type, TypeOverride, Value},
ErrorKind,
};

Expand Down Expand Up @@ -45,7 +45,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, TypeId>>,
type_guard_stack: Vec<HashMap<GuardPath, TypeOverride>>,

// 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 @@ -210,13 +210,13 @@ impl<'a> Compiler<'a> {
format!("fun({}) -> {}", params.join(", "), ret)
}
Type::Alias(..) => unreachable!(),
Type::Optional(ty) => {
Type::Nullable(ty) => {
let inner = self.type_name_visitor(*ty, stack);
format!("{inner}?")
}
Type::PossiblyUndefined(ty) => {
Type::Optional(ty) => {
let inner = self.type_name_visitor(*ty, stack);
format!("possibly undefined {inner}")
format!("optional {inner}")
}
};

Expand Down Expand Up @@ -261,7 +261,7 @@ impl<'a> Compiler<'a> {
Value::new(self.builtins.unknown_hir, self.builtins.unknown)
}

fn symbol_type(&self, guard_path: &GuardPath) -> Option<TypeId> {
fn symbol_type(&self, guard_path: &GuardPath) -> Option<TypeOverride> {
for guards in &self.type_guard_stack {
if let Some(guard) = guards.get(guard_path) {
return Some(*guard);
Expand All @@ -270,6 +270,13 @@ 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
2 changes: 2 additions & 0 deletions crates/rue-compiler/src/compiler/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use super::Compiler;
mod binary_expr;
mod block_expr;
mod cast_expr;
mod exists_expr;
mod field_access_expr;
mod function_call_expr;
mod group_expr;
Expand Down Expand Up @@ -47,6 +48,7 @@ impl Compiler<'_> {
Expr::FunctionCallExpr(call) => self.compile_function_call_expr(call),
Expr::FieldAccessExpr(field_access) => self.compile_field_access_expr(field_access),
Expr::IndexAccessExpr(index_access) => self.compile_index_access_expr(index_access),
Expr::ExistsExpr(exists) => self.compile_exists_expr(exists),
};

self.is_callee = false;
Expand Down
20 changes: 11 additions & 9 deletions crates/rue-compiler/src/compiler/expr/binary_expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use rue_parser::{AstNode, BinaryExpr, BinaryOp, Expr};
use crate::{
compiler::Compiler,
hir::{BinOp, Hir, Op},
value::{Guard, Value},
value::{Guard, TypeOverride, Value},
ErrorKind, HirId, TypeId,
};

Expand Down Expand Up @@ -172,10 +172,11 @@ impl Compiler<'_> {
{
if let Some(guard_path) = rhs.guard_path {
let then_type = self.builtins.nil;
let else_type = self.db.non_optional(rhs.type_id);
value
.guards
.insert(guard_path, Guard::new(then_type, else_type));
let else_type = self.db.non_nullable(rhs.type_id);
value.guards.insert(
guard_path,
Guard::new(TypeOverride::new(then_type), TypeOverride::new(else_type)),
);
}
}

Expand All @@ -186,10 +187,11 @@ impl Compiler<'_> {
{
if let Some(guard_path) = lhs.guard_path.clone() {
let then_type = self.builtins.nil;
let else_type = self.db.non_optional(lhs.type_id);
value
.guards
.insert(guard_path, Guard::new(then_type, else_type));
let else_type = self.db.non_nullable(lhs.type_id);
value.guards.insert(
guard_path,
Guard::new(TypeOverride::new(then_type), TypeOverride::new(else_type)),
);
}
}

Expand Down
52 changes: 38 additions & 14 deletions crates/rue-compiler/src/compiler/expr/exists_expr.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,38 @@
/*
Type::PossiblyUndefined(inner) if field_name.text() == "exists" => {
let maybe_nil_reference = self.db.alloc_hir(Hir::Op(Op::Exists, old_value.hir_id));
let exists = self.db.alloc_hir(Hir::Op(Op::Listp, maybe_nil_reference));
let mut new_value = Value::new(exists, self.builtins.bool);

if let Some(guard_path) = old_value.guard_path {
new_value
.guards
.insert(guard_path, Guard::new(inner, old_value.type_id));
}

new_value
} */
use rue_parser::{AstNode, ExistsExpr};

use crate::{
compiler::Compiler,
hir::{Hir, Op},
value::{Guard, Mutation, Type, TypeOverride, Value},
ErrorKind,
};

impl Compiler<'_> {
pub fn compile_exists_expr(&mut self, exists: &ExistsExpr) -> Value {
let Some(value) = exists.expr().map(|expr| self.compile_expr(&expr, None)) else {
return self.unknown();
};

let Type::Optional(inner) = self.db.ty(value.type_id).clone() else {
self.db.error(
ErrorKind::InvalidExistanceCheck(self.type_name(value.type_id)),
exists.syntax().text_range(),
);
return self.unknown();
};

let exists = self.db.alloc_hir(Hir::Op(Op::Listp, value.hir_id));
let mut new_value = Value::new(exists, self.builtins.bool);

if let Some(guard_path) = value.guard_path {
let mut unwrap = TypeOverride::new(inner);
unwrap.mutation = Mutation::UnwrapOptional;
new_value.guards.insert(
guard_path,
Guard::new(unwrap, TypeOverride::new(value.type_id)),
);
}

new_value
}
}
14 changes: 7 additions & 7 deletions crates/rue-compiler/src/compiler/expr/field_access_expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,14 @@ impl Compiler<'_> {
let mut type_id = field_type;

if index == struct_type.fields.len() - 1 && struct_type.rest == Rest::Optional {
type_id = self.db.alloc_type(Type::PossiblyUndefined(type_id));
type_id = self.db.alloc_type(Type::Optional(type_id));
}

Value::new(
self.compile_index(
old_value.hir_id,
index,
index == struct_type.fields.len() - 1
&& struct_type.rest == Rest::Spread,
index == struct_type.fields.len() - 1 && struct_type.rest != Rest::Nil,
),
type_id,
)
Expand All @@ -57,14 +56,14 @@ impl Compiler<'_> {
let mut type_id = field_type;

if index == fields.len() - 1 && variant_type.rest == Rest::Optional {
type_id = self.db.alloc_type(Type::PossiblyUndefined(type_id));
type_id = self.db.alloc_type(Type::Optional(type_id));
}

Value::new(
self.compile_index(
old_value.hir_id,
index,
index == fields.len() - 1 && variant_type.rest == Rest::Spread,
index == fields.len() - 1 && variant_type.rest != Rest::Nil,
),
type_id,
)
Expand Down Expand Up @@ -113,8 +112,9 @@ impl Compiler<'_> {
};

if let Some(guard_path) = new_value.guard_path.as_ref() {
if let Some(type_id) = self.symbol_type(guard_path) {
new_value.type_id = type_id;
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);
}
}

Expand Down
45 changes: 36 additions & 9 deletions crates/rue-compiler/src/compiler/expr/guard_expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use rue_parser::{AstNode, GuardExpr};
use crate::{
compiler::Compiler,
hir::{BinOp, Hir, Op},
value::{Guard, PairType, Type, Value},
value::{Guard, PairType, Type, TypeOverride, Value},
Comparison, ErrorKind, HirId, TypeId, WarningKind,
};

Expand Down Expand Up @@ -52,7 +52,10 @@ impl Compiler<'_> {
WarningKind::RedundantTypeCheck(self.type_name(from)),
text_range,
);
return Some((Guard::new(to, self.builtins.bool), hir_id));
return Some((
Guard::new(TypeOverride::new(to), TypeOverride::new(self.builtins.bool)),
hir_id,
));
}

match (self.db.ty(from).clone(), self.db.ty(to).clone()) {
Expand All @@ -66,7 +69,13 @@ impl Compiler<'_> {
}

let hir_id = self.db.alloc_hir(Hir::Op(Op::Listp, hir_id));
Some((Guard::new(to, self.builtins.bytes), hir_id))
Some((
Guard::new(
TypeOverride::new(to),
TypeOverride::new(self.builtins.bytes),
),
hir_id,
))
}
(Type::Any, Type::Bytes) => {
let pair_type = self.db.alloc_type(Type::Pair(PairType {
Expand All @@ -75,7 +84,10 @@ impl Compiler<'_> {
}));
let is_cons = self.db.alloc_hir(Hir::Op(Op::Listp, hir_id));
let hir_id = self.db.alloc_hir(Hir::Op(Op::Not, is_cons));
Some((Guard::new(to, pair_type), hir_id))
Some((
Guard::new(TypeOverride::new(to), TypeOverride::new(pair_type)),
hir_id,
))
}
(Type::List(inner), Type::Pair(PairType { first, rest })) => {
if !self.db.compare_type(first, inner).is_equal() {
Expand All @@ -87,7 +99,10 @@ impl Compiler<'_> {
}

let hir_id = self.db.alloc_hir(Hir::Op(Op::Listp, hir_id));
Some((Guard::new(to, self.builtins.nil), hir_id))
Some((
Guard::new(TypeOverride::new(to), TypeOverride::new(self.builtins.nil)),
hir_id,
))
}
(Type::List(inner), Type::Nil) => {
let pair_type = self.db.alloc_type(Type::Pair(PairType {
Expand All @@ -96,23 +111,32 @@ impl Compiler<'_> {
}));
let is_cons = self.db.alloc_hir(Hir::Op(Op::Listp, hir_id));
let hir_id = self.db.alloc_hir(Hir::Op(Op::Not, is_cons));
Some((Guard::new(to, pair_type), hir_id))
Some((
Guard::new(TypeOverride::new(to), TypeOverride::new(pair_type)),
hir_id,
))
}
(Type::Bytes, Type::Bytes32) => {
let strlen = self.db.alloc_hir(Hir::Op(Op::Strlen, hir_id));
let length = self.db.alloc_hir(Hir::Atom(vec![32]));
let hir_id = self
.db
.alloc_hir(Hir::BinaryOp(BinOp::Equals, strlen, length));
Some((Guard::new(to, from), hir_id))
Some((
Guard::new(TypeOverride::new(to), TypeOverride::new(from)),
hir_id,
))
}
(Type::Bytes, Type::PublicKey) => {
let strlen = self.db.alloc_hir(Hir::Op(Op::Strlen, hir_id));
let length = self.db.alloc_hir(Hir::Atom(vec![48]));
let hir_id = self
.db
.alloc_hir(Hir::BinaryOp(BinOp::Equals, strlen, length));
Some((Guard::new(to, from), hir_id))
Some((
Guard::new(TypeOverride::new(to), TypeOverride::new(from)),
hir_id,
))
}
(Type::Enum(..), Type::EnumVariant(variant_type)) => {
if variant_type.enum_type != from {
Expand All @@ -128,7 +152,10 @@ impl Compiler<'_> {
first,
variant_type.discriminant,
));
Some((Guard::new(to, from), hir_id))
Some((
Guard::new(TypeOverride::new(to), TypeOverride::new(from)),
hir_id,
))
}
_ => {
self.db.error(
Expand Down
8 changes: 3 additions & 5 deletions crates/rue-compiler/src/compiler/expr/initializer_expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use rue_parser::{AstNode, InitializerExpr, InitializerField};

use crate::{
compiler::Compiler,
hir::{Hir, Op},
hir::Hir,
value::{Rest, Type, Value},
ErrorKind, HirId, TypeId,
};
Expand Down Expand Up @@ -93,7 +93,7 @@ impl Compiler<'_> {
if rest == Rest::Optional
&& struct_fields.get_index_of(name.text()) == Some(struct_fields.len() - 1)
{
optional |= matches!(self.db.ty(value.type_id), Type::PossiblyUndefined(..));
optional |= matches!(self.db.ty(value.type_id), Type::Optional(..));
value.type_id = self.db.non_undefined(value.type_id);
}

Expand Down Expand Up @@ -152,10 +152,8 @@ impl Compiler<'_> {

let field = value.unwrap_or(self.builtins.unknown_hir);

if i == 0 && rest == Rest::Spread {
if i == 0 && (rest == Rest::Spread || (rest == Rest::Optional && optional)) {
hir_id = field;
} else if i == 0 && rest == Rest::Optional && optional {
hir_id = self.db.alloc_hir(Hir::Op(Op::Exists, field));
} else {
hir_id = self.db.alloc_hir(Hir::Pair(field, hir_id));
}
Expand Down
22 changes: 12 additions & 10 deletions crates/rue-compiler/src/compiler/expr/path_expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,26 +62,28 @@ impl Compiler<'_> {
return self.unknown();
}

let override_type_id = self.symbol_type(&GuardPath::new(symbol_id));
let type_override = self.symbol_type(&GuardPath::new(symbol_id));
let override_type_id = type_override.map(|ty| ty.type_id);
let mut reference = self.db.alloc_hir(Hir::Reference(symbol_id, text_range));

if let Some(mutation) = type_override.map(|ty| ty.mutation) {
reference = self.apply_mutation(reference, mutation);
}

let mut value = match self.db.symbol(symbol_id).clone() {
Symbol::Unknown | Symbol::Module(..) => unreachable!(),
Symbol::Function(Function { ty, .. }) | Symbol::InlineFunction(Function { ty, .. }) => {
let type_id = self.db.alloc_type(Type::Function(ty.clone()));
Value::new(
self.db.alloc_hir(Hir::Reference(symbol_id, text_range)),
override_type_id.unwrap_or(type_id),
)
Value::new(reference, override_type_id.unwrap_or(type_id))
}
Symbol::Parameter(type_id) => {
Value::new(reference, override_type_id.unwrap_or(type_id))
}
Symbol::Parameter(type_id) => Value::new(
self.db.alloc_hir(Hir::Reference(symbol_id, text_range)),
override_type_id.unwrap_or(type_id),
),
Symbol::Let(mut value) | Symbol::Const(mut value) | Symbol::InlineConst(mut value) => {
if let Some(type_id) = override_type_id {
value.type_id = type_id;
}
value.hir_id = self.db.alloc_hir(Hir::Reference(symbol_id, text_range));
value.hir_id = reference;
value
}
};
Expand Down
Loading

0 comments on commit e0d5e6c

Please sign in to comment.