From 033835fbf042f172e3fb6f2601ac832794e0c306 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Tue, 25 Jun 2024 19:51:07 -0400 Subject: [PATCH] Make unit enums work --- crates/rue-compiler/src/compiler.rs | 48 ++++++++++--------- .../src/compiler/expr/field_access_expr.rs | 12 ++--- .../src/compiler/expr/initializer_expr.rs | 38 +++++++++------ .../src/compiler/expr/path_expr.rs | 7 +-- .../src/compiler/item/enum_item.rs | 6 ++- .../rue-compiler/src/database/type_system.rs | 23 ++++----- crates/rue-compiler/src/error.rs | 5 +- crates/rue-compiler/src/value/ty.rs | 2 +- std/stdlib.rue | 10 ++-- tests.toml | 10 ++++ tests/types/unit_variant.rue | 10 ++++ 11 files changed, 100 insertions(+), 71 deletions(-) create mode 100644 tests/types/unit_variant.rue diff --git a/crates/rue-compiler/src/compiler.rs b/crates/rue-compiler/src/compiler.rs index 0126f23..f1d9e8e 100644 --- a/crates/rue-compiler/src/compiler.rs +++ b/crates/rue-compiler/src/compiler.rs @@ -164,7 +164,7 @@ impl<'a> Compiler<'a> { }) .collect(); - format!("{{ {} }}", fields.join(", ")) + format!("{{{}}}", fields.join(", ")) } else { self.type_name_visitor(struct_type.original_type_id, stack) } @@ -173,29 +173,31 @@ impl<'a> Compiler<'a> { Type::EnumVariant(enum_variant) => { let enum_name = self.type_name_visitor(enum_variant.enum_type, stack); - let fields: Vec = enum_variant - .fields - .iter() - .map(|(name, ty)| format!("{}: {}", name, self.type_name_visitor(*ty, stack))) - .collect(); + let fields: Option> = enum_variant.fields.as_ref().map(|fields| { + fields + .iter() + .map(|(name, ty)| { + format!("{}: {}", name, self.type_name_visitor(*ty, stack)) + }) + .collect() + }); - format!( - "{}::{} {{ {} }}", - enum_name, - match self.db.ty(enum_variant.enum_type) { - Type::Enum(enum_type) => { - enum_type - .variants - .iter() - .find(|item| *item.1 == enum_variant.original_type_id) - .expect("enum type is missing variant") - .0 - .clone() - } - _ => unreachable!(), - }, - fields.join(", ") - ) + let variant_name = match self.db.ty(enum_variant.enum_type) { + Type::Enum(enum_type) => enum_type + .variants + .iter() + .find(|item| *item.1 == enum_variant.original_type_id) + .expect("enum type is missing variant") + .0 + .clone(), + _ => unreachable!(), + }; + + if let Some(fields) = fields { + format!("{enum_name}::{variant_name} {{{}}}", fields.join(", ")) + } else { + format!("{enum_name}::{variant_name}") + } } Type::Function(function_type) => { let params: Vec = function_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 6442d18..b26f4fe 100644 --- a/crates/rue-compiler/src/compiler/expr/field_access_expr.rs +++ b/crates/rue-compiler/src/compiler/expr/field_access_expr.rs @@ -54,13 +54,12 @@ impl Compiler<'_> { } } Type::EnumVariant(variant_type) => { - if let Some((index, _, &field_type)) = - variant_type.fields.get_full(field_name.text()) - { + let fields = variant_type.fields.unwrap_or_default(); + + if let Some((index, _, &field_type)) = fields.get_full(field_name.text()) { let mut type_id = field_type; - if index == variant_type.fields.len() - 1 && variant_type.rest == Rest::Optional - { + if index == fields.len() - 1 && variant_type.rest == Rest::Optional { type_id = self.db.alloc_type(Type::PossiblyUndefined(type_id)); } @@ -68,8 +67,7 @@ impl Compiler<'_> { self.compile_index( old_value.hir_id, index, - index == variant_type.fields.len() - 1 - && variant_type.rest == Rest::Spread, + index == fields.len() - 1 && variant_type.rest == Rest::Spread, ), type_id, ) diff --git a/crates/rue-compiler/src/compiler/expr/initializer_expr.rs b/crates/rue-compiler/src/compiler/expr/initializer_expr.rs index d1052fb..d59ee0d 100644 --- a/crates/rue-compiler/src/compiler/expr/initializer_expr.rs +++ b/crates/rue-compiler/src/compiler/expr/initializer_expr.rs @@ -33,21 +33,29 @@ impl Compiler<'_> { } } Some(Type::EnumVariant(enum_variant)) => { - let fields_hir_id = self.compile_initializer_fields( - ty.unwrap(), - &enum_variant.fields, - enum_variant.rest, - initializer.fields(), - initializer.syntax().text_range(), - ); - - let hir_id = self - .db - .alloc_hir(Hir::Pair(enum_variant.discriminant, fields_hir_id)); - - match ty { - Some(struct_type) => Value::new(hir_id, struct_type), - None => self.unknown(), + if let Some(fields) = enum_variant.fields { + let fields_hir_id = self.compile_initializer_fields( + ty.unwrap(), + &fields, + enum_variant.rest, + initializer.fields(), + initializer.syntax().text_range(), + ); + + let hir_id = self + .db + .alloc_hir(Hir::Pair(enum_variant.discriminant, fields_hir_id)); + + match ty { + Some(struct_type) => Value::new(hir_id, struct_type), + None => self.unknown(), + } + } else { + self.db.error( + ErrorKind::EnumVariantWithoutFields, + initializer.path().unwrap().syntax().text_range(), + ); + self.unknown() } } Some(_) => { diff --git a/crates/rue-compiler/src/compiler/expr/path_expr.rs b/crates/rue-compiler/src/compiler/expr/path_expr.rs index de8f7d0..753422b 100644 --- a/crates/rue-compiler/src/compiler/expr/path_expr.rs +++ b/crates/rue-compiler/src/compiler/expr/path_expr.rs @@ -33,14 +33,9 @@ impl Compiler<'_> { PathItem::Symbol(symbol_id) => symbol_id, PathItem::Type(type_id) => { if let Type::EnumVariant(variant_type) = self.db.ty(type_id).clone() { - let Type::Enum(enum_type) = self.db.ty(variant_type.enum_type) else { - unreachable!(); - }; - - if enum_type.has_fields { + if variant_type.fields.is_some() { self.db.error(ErrorKind::EnumVariantWithFields, text_range); } - return Value::new(variant_type.discriminant, type_id); } self.db.error(ErrorKind::ExpectedSymbolPath, text_range); diff --git a/crates/rue-compiler/src/compiler/item/enum_item.rs b/crates/rue-compiler/src/compiler/item/enum_item.rs index 168a4c3..e3f6edb 100644 --- a/crates/rue-compiler/src/compiler/item/enum_item.rs +++ b/crates/rue-compiler/src/compiler/item/enum_item.rs @@ -148,7 +148,11 @@ impl Compiler<'_> { *self.db.ty_mut(variant_type_id) = Type::EnumVariant(EnumVariantType { enum_type: enum_type_id, original_type_id: variant_type_id, - fields, + fields: if variant.fields().is_some() { + Some(fields) + } else { + None + }, rest, discriminant, }); diff --git a/crates/rue-compiler/src/database/type_system.rs b/crates/rue-compiler/src/database/type_system.rs index 8c0fdf5..a08cdeb 100644 --- a/crates/rue-compiler/src/database/type_system.rs +++ b/crates/rue-compiler/src/database/type_system.rs @@ -137,16 +137,17 @@ impl Database { let new_variant = EnumVariantType { enum_type: enum_variant.enum_type, original_type_id: enum_variant.original_type_id, - fields: enum_variant - .fields - .iter() - .map(|(k, v)| { - ( - k.clone(), - self.substitute_type_visitor(*v, substitutions, visited), - ) - }) - .collect(), + fields: enum_variant.fields.as_ref().map(|fields| { + fields + .iter() + .map(|(k, v)| { + ( + k.clone(), + self.substitute_type_visitor(*v, substitutions, visited), + ) + }) + .collect() + }), rest: enum_variant.rest, discriminant: enum_variant.discriminant, }; @@ -755,7 +756,7 @@ mod tests { *db.ty_mut(variant) = Type::EnumVariant(EnumVariantType { enum_type, original_type_id: variant, - fields: fields(&[ty.int]), + fields: Some(fields(&[ty.int])), discriminant: ty.unknown_hir, rest: Rest::Nil, }); diff --git a/crates/rue-compiler/src/error.rs b/crates/rue-compiler/src/error.rs index 711109e..18a723b 100644 --- a/crates/rue-compiler/src/error.rs +++ b/crates/rue-compiler/src/error.rs @@ -188,9 +188,12 @@ pub enum ErrorKind { #[error("enum discriminant too large")] EnumDiscriminantTooLarge, - #[error("cannot reference enum variants of enums with fields")] + #[error("cannot directly reference enum variants with fields")] EnumVariantWithFields, + #[error("cannot initialize enum variants without fields")] + EnumVariantWithoutFields, + #[error("unknown enum variant `{0}`")] UnknownEnumVariant(String), diff --git a/crates/rue-compiler/src/value/ty.rs b/crates/rue-compiler/src/value/ty.rs index 49b5c2b..640d662 100644 --- a/crates/rue-compiler/src/value/ty.rs +++ b/crates/rue-compiler/src/value/ty.rs @@ -47,7 +47,7 @@ pub struct EnumType { pub struct EnumVariantType { pub enum_type: TypeId, pub original_type_id: TypeId, - pub fields: IndexMap, + pub fields: Option>, pub rest: Rest, pub discriminant: HirId, } diff --git a/std/stdlib.rue b/std/stdlib.rue index 14a2b6a..e9f7d15 100644 --- a/std/stdlib.rue +++ b/std/stdlib.rue @@ -1,7 +1,7 @@ export enum Condition { Remark = 1 { - // TODO: Optional value + value?: Any, }, AggSigParent = 43 { public_key: PublicKey, @@ -38,7 +38,7 @@ export enum Condition { CreateCoin = 51 { puzzle_hash: Bytes32, amount: Int, - // TODO: Optional memos + memos?: Bytes[], }, ReserveFee = 52 { amount: Int, @@ -79,9 +79,7 @@ export enum Condition { AssertMyBirthHeight = 75 { height: Int, }, - AssertEphemeral = 76 { - // TODO: Unit variants and enum repr - }, + AssertEphemeral = 76, AssertSecondsRelative = 80 { seconds: Int, }, @@ -108,7 +106,7 @@ export enum Condition { }, Softfork = 90 { cost: Int, - // TODO: Rest args with type `Any` + value?: Any, }, } diff --git a/tests.toml b/tests.toml index 1871e67..ceb1e21 100644 --- a/tests.toml +++ b/tests.toml @@ -364,3 +364,13 @@ input = "()" output = "()" hash = "42840c6aebec47ce2e01629ce381b461c19695264281a7b1aab5d4ff54506775" error = "()" + +[unit_variant] +parser_errors = [] +compiler_errors = [ + "unused let binding `non_unit` at 7:9", + "unused let binding `unit` at 8:9", + "unused enum variant `NonUnit` at 2:5", + "unused enum variant `Unit` at 3:5", + "unused enum `Enum` at 1:6", +] diff --git a/tests/types/unit_variant.rue b/tests/types/unit_variant.rue new file mode 100644 index 0000000..bcc3b03 --- /dev/null +++ b/tests/types/unit_variant.rue @@ -0,0 +1,10 @@ +enum Enum { + NonUnit {}, + Unit, +} + +fun main() -> Int { + let non_unit = Enum::NonUnit {}; + let unit = Enum::Unit; + 0 +}