From 9557f08a98f89c36c6e5231afea0e5254df81422 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Sat, 3 Aug 2024 15:01:00 -0400 Subject: [PATCH] Initial simplification of type guards --- crates/rue-compiler/src/compiler.rs | 30 +-- .../src/compiler/expr/binary_expr.rs | 16 +- .../src/compiler/expr/field_access_expr.rs | 237 ++++++++++-------- .../src/compiler/expr/guard_expr.rs | 8 +- .../src/compiler/expr/path_expr.rs | 13 +- .../rue-compiler/src/compiler/stmt/if_stmt.rs | 4 +- crates/rue-compiler/src/value.rs | 64 +++-- crates/rue-compiler/src/value/guard.rs | 51 ---- crates/rue-compiler/src/value/guard_path.rs | 23 -- crates/rue-typing/src/type_path.rs | 11 + 10 files changed, 219 insertions(+), 238 deletions(-) delete mode 100644 crates/rue-compiler/src/value/guard.rs delete mode 100644 crates/rue-compiler/src/value/guard_path.rs diff --git a/crates/rue-compiler/src/compiler.rs b/crates/rue-compiler/src/compiler.rs index d2a703b..756c003 100644 --- a/crates/rue-compiler/src/compiler.rs +++ b/crates/rue-compiler/src/compiler.rs @@ -1,6 +1,6 @@ #![allow(clippy::map_unwrap_or)] -use rue_typing::HashMap; +use rue_typing::{HashMap, TypePath}; pub(crate) use builtins::Builtins; @@ -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, }; @@ -47,7 +47,7 @@ pub struct Compiler<'a> { type_definition_stack: Vec, // The type guard stack is used for overriding types in certain contexts. - type_guard_stack: Vec>, + type_guard_stack: Vec>, // The generic type stack is used for overriding generic types that are being checked against. generic_type_stack: Vec>, @@ -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) { @@ -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 { + fn symbol_type(&self, guard_path: &GuardPath) -> Option { for guards in self.type_guard_stack.iter().rev() { if let Some(guard) = guards.get(guard_path) { return Some(*guard); @@ -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")) diff --git a/crates/rue-compiler/src/compiler/expr/binary_expr.rs b/crates/rue-compiler/src/compiler/expr/binary_expr.rs index ddca43c..1272f0e 100644 --- a/crates/rue-compiler/src/compiler/expr/binary_expr.rs +++ b/crates/rue-compiler/src/compiler/expr/binary_expr.rs @@ -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, }; @@ -170,10 +170,9 @@ 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)); } } @@ -181,10 +180,9 @@ impl Compiler<'_> { 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)); } } diff --git a/crates/rue-compiler/src/compiler/expr/field_access_expr.rs b/crates/rue-compiler/src/compiler/expr/field_access_expr.rs index 9d51ef8..dffcc67 100644 --- a/crates/rue-compiler/src/compiler/expr/field_access_expr.rs +++ b/crates/rue-compiler/src/compiler/expr/field_access_expr.rs @@ -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, }; @@ -18,120 +18,42 @@ 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(); } @@ -139,11 +61,126 @@ impl Compiler<'_> { 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 { + 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 { + 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 { + 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) + } } diff --git a/crates/rue-compiler/src/compiler/expr/guard_expr.rs b/crates/rue-compiler/src/compiler/expr/guard_expr.rs index 6e3a66a..8f2d51e 100644 --- a/crates/rue-compiler/src/compiler/expr/guard_expr.rs +++ b/crates/rue-compiler/src/compiler/expr/guard_expr.rs @@ -4,7 +4,7 @@ use rue_typing::{bigint_to_bytes, Check, TypeId}; use crate::{ compiler::Compiler, hir::{BinOp, Hir, Op}, - value::{Guard, TypeOverride, Value}, + value::{Guard, Value}, ErrorKind, HirId, WarningKind, }; @@ -62,11 +62,7 @@ impl Compiler<'_> { if let Some(guard_path) = expr.guard_path { let difference = self.ty.difference(expr.type_id, rhs); - - value.guards.insert( - guard_path, - Guard::new(TypeOverride::new(rhs), TypeOverride::new(difference)), - ); + value.guards.insert(guard_path, Guard::new(rhs, difference)); } value diff --git a/crates/rue-compiler/src/compiler/expr/path_expr.rs b/crates/rue-compiler/src/compiler/expr/path_expr.rs index 0c19033..f857f4d 100644 --- a/crates/rue-compiler/src/compiler/expr/path_expr.rs +++ b/crates/rue-compiler/src/compiler/expr/path_expr.rs @@ -76,22 +76,15 @@ impl Compiler<'_> { } 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 reference = self.db.alloc_hir(Hir::Reference(symbol_id, text_range)); let mut value = match self.db.symbol(symbol_id).clone() { Symbol::Unknown | Symbol::Module(..) => unreachable!(), Symbol::Function(Function { type_id, .. }) | Symbol::InlineFunction(Function { type_id, .. }) - | Symbol::Parameter(type_id) => { - Value::new(reference, override_type_id.unwrap_or(type_id)) - } + | Symbol::Parameter(type_id) => Value::new(reference, type_override.unwrap_or(type_id)), Symbol::Let(mut value) | Symbol::Const(mut value) | Symbol::InlineConst(mut value) => { - if let Some(type_id) = override_type_id { + if let Some(type_id) = type_override { value.type_id = type_id; } value.hir_id = reference; diff --git a/crates/rue-compiler/src/compiler/stmt/if_stmt.rs b/crates/rue-compiler/src/compiler/stmt/if_stmt.rs index f131ee7..9927551 100644 --- a/crates/rue-compiler/src/compiler/stmt/if_stmt.rs +++ b/crates/rue-compiler/src/compiler/stmt/if_stmt.rs @@ -6,7 +6,7 @@ use rue_typing::TypeId; use crate::{ compiler::{block::BlockTerminator, Compiler}, scope::Scope, - value::{GuardPath, TypeOverride}, + value::GuardPath, ErrorKind, HirId, }; @@ -16,7 +16,7 @@ impl Compiler<'_> { &mut self, if_stmt: &IfStmt, expected_type: Option, - ) -> (HirId, HirId, HashMap) { + ) -> (HirId, HirId, HashMap) { // Compile the condition expression. let condition = if_stmt .condition() diff --git a/crates/rue-compiler/src/value.rs b/crates/rue-compiler/src/value.rs index 4e6bb48..89b61ac 100644 --- a/crates/rue-compiler/src/value.rs +++ b/crates/rue-compiler/src/value.rs @@ -1,14 +1,12 @@ -use rue_typing::HashMap; - -mod guard; -mod guard_path; +use std::ops::Not; -pub use guard::*; -pub use guard_path::*; +use rue_typing::HashMap; use rue_typing::TypeId; +use rue_typing::TypePath; use crate::HirId; +use crate::SymbolId; #[derive(Debug, Clone)] pub struct Value { @@ -28,28 +26,58 @@ impl Value { } } - pub fn then_guards(&self) -> HashMap { + pub fn then_guards(&self) -> HashMap { self.guards .iter() - .map(|(k, v)| (k.clone(), v.then_type)) + .map(|(guard_path, guard)| (guard_path.clone(), guard.then_type)) .collect() } - pub fn else_guards(&self) -> HashMap { + pub fn else_guards(&self) -> HashMap { self.guards .iter() - .map(|(k, v)| (k.clone(), v.else_type)) + .map(|(guard_path, guard)| (guard_path.clone(), guard.else_type)) .collect() } +} + +#[derive(Debug, Clone, Copy)] +pub struct Guard { + pub then_type: TypeId, + pub else_type: TypeId, +} + +impl Guard { + pub fn new(then_type: TypeId, else_type: TypeId) -> Self { + Self { + then_type, + else_type, + } + } +} + +impl Not for Guard { + type Output = Self; + + fn not(self) -> Self::Output { + Self { + then_type: self.else_type, + else_type: self.then_type, + } + } +} - pub fn extend_guard_path(mut self, old_value: Value, item: GuardPathItem) -> Self { - match old_value.guard_path { - Some(mut path) => { - path.items.push(item); - self.guard_path = Some(path); - self - } - None => self, +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct GuardPath { + pub symbol_id: SymbolId, + pub items: Vec, +} + +impl GuardPath { + pub fn new(symbol_id: SymbolId) -> Self { + Self { + symbol_id, + items: Vec::new(), } } } diff --git a/crates/rue-compiler/src/value/guard.rs b/crates/rue-compiler/src/value/guard.rs deleted file mode 100644 index 302b82f..0000000 --- a/crates/rue-compiler/src/value/guard.rs +++ /dev/null @@ -1,51 +0,0 @@ -use std::ops::Not; - -use rue_typing::TypeId; - -#[derive(Debug, Clone, Copy)] -pub struct Guard { - pub then_type: TypeOverride, - pub else_type: TypeOverride, -} - -impl Guard { - pub fn new(then_type: TypeOverride, else_type: TypeOverride) -> Self { - Self { - then_type, - else_type, - } - } -} - -impl Not for Guard { - type Output = Self; - - fn not(self) -> Self::Output { - Self { - then_type: self.else_type, - else_type: self.then_type, - } - } -} - -#[derive(Debug, Clone, Copy)] -pub struct TypeOverride { - pub type_id: TypeId, - pub mutation: Mutation, -} - -impl TypeOverride { - pub fn new(type_id: TypeId) -> Self { - Self { - type_id, - mutation: Mutation::None, - } - } -} - -#[derive(Debug, Default, Clone, Copy)] -pub enum Mutation { - #[default] - None, - UnwrapOptional, -} diff --git a/crates/rue-compiler/src/value/guard_path.rs b/crates/rue-compiler/src/value/guard_path.rs deleted file mode 100644 index 9b1e9a1..0000000 --- a/crates/rue-compiler/src/value/guard_path.rs +++ /dev/null @@ -1,23 +0,0 @@ -use crate::SymbolId; - -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct GuardPath { - pub symbol_id: SymbolId, - pub items: Vec, -} - -impl GuardPath { - pub fn new(symbol_id: SymbolId) -> Self { - Self { - symbol_id, - items: Vec::new(), - } - } -} - -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub enum GuardPathItem { - Field(String), - First, - Rest, -} diff --git a/crates/rue-typing/src/type_path.rs b/crates/rue-typing/src/type_path.rs index a557a1a..2cf8f2e 100644 --- a/crates/rue-typing/src/type_path.rs +++ b/crates/rue-typing/src/type_path.rs @@ -3,3 +3,14 @@ pub enum TypePath { First, Rest, } + +pub fn index_to_path(index: usize, nil_terminated: bool) -> Vec { + let mut path = Vec::with_capacity(index); + for _ in 0..index { + path.push(TypePath::Rest); + } + if nil_terminated { + path.push(TypePath::First); + } + path +}