diff --git a/compiler/src/passes/validate/constrain/expr.rs b/compiler/src/passes/validate/constrain/expr.rs index edd9efd..7acb24d 100644 --- a/compiler/src/passes/validate/constrain/expr.rs +++ b/compiler/src/passes/validate/constrain/expr.rs @@ -17,6 +17,7 @@ use crate::passes::validate::constrain::seq::constrain_seq; use crate::passes::validate::constrain::unary_op::constrain_unary_op; use crate::passes::validate::constrain::uncover_globals::Env; use crate::passes::validate::constrain::var::constrain_var; +use crate::passes::validate::constrain::variant; use crate::passes::validate::error::TypeError; use crate::passes::validate::{ExprConstrained, ExprUniquified}; @@ -52,7 +53,11 @@ pub fn constrain_expr<'p>( Expr::Struct { sym, fields } => constrain_struct(env, span, sym, fields), Expr::AccessField { strct, field } => constrain_access_field(env, span, *strct, field), Expr::Asm { instrs } => constrain_asm(env, span, instrs), - Expr::Variant { .. } => todo!(), + Expr::Variant { + enum_sym, + variant_sym, + bdy, + } => variant::constraint_variant(env, span, enum_sym, variant_sym, *bdy), Expr::Switch { .. } => todo!(), } } diff --git a/compiler/src/passes/validate/constrain/mod.rs b/compiler/src/passes/validate/constrain/mod.rs index e7b2578..55db8d6 100644 --- a/compiler/src/passes/validate/constrain/mod.rs +++ b/compiler/src/passes/validate/constrain/mod.rs @@ -28,6 +28,7 @@ mod r#struct; mod unary_op; mod uncover_globals; mod var; +mod variant; impl<'p> PrgUniquified<'p> { pub fn constrain(self) -> Result, TypeError> { diff --git a/compiler/src/passes/validate/constrain/variant.rs b/compiler/src/passes/validate/constrain/variant.rs new file mode 100644 index 0000000..b53a507 --- /dev/null +++ b/compiler/src/passes/validate/constrain/variant.rs @@ -0,0 +1,65 @@ +use crate::passes::parse::{Constrained, Span, Spanned, TypeDef}; +use crate::passes::validate::constrain::expr::constrain_expr; +use crate::passes::validate::constrain::uncover_globals::{Env, EnvEntry}; +use crate::passes::validate::error::TypeError; +use crate::passes::validate::partial_type::PartialType; +use crate::passes::validate::{ExprConstrained, ExprUniquified, MetaConstrained}; +use crate::utils::unique_sym::UniqueSym; + +pub fn constraint_variant<'p>( + env: &mut Env<'_, 'p>, + span: Span, + enum_sym: Spanned>, + variant_sym: Spanned<&'p str>, + bdy: Spanned>, +) -> Result>, TypeError> { + // Get the `EnvEntry` from the scope. + // This should exist after uniquify, but could potentially not be an enum definition. + let EnvEntry::Def { + def: TypeDef::Enum { + variants: def_variants, + }, + } = &env.scope[&enum_sym.inner] + else { + return Err(TypeError::SymbolShouldBeEnum { span: enum_sym.meta }); + }; + + // Check if variant_sym exists + let Some((def_span, variant_typ)) = def_variants + .iter() + .find(|(def_variant, _)| def_variant.inner == variant_sym.inner) + else { + return Err(TypeError::UnknownEnumVariant { + sym: variant_sym.inner.to_string(), + span: variant_sym.meta, + }); + }; + let def_span = def_span.meta; + let variant_typ = variant_typ.clone(); + + // Check body type + let bdy_typ = constrain_expr(bdy, env)?; + env.uf.expect_type( + bdy_typ.meta.index, + variant_typ, + |field_typ, def_typ| TypeError::MismatchedStructField { + expect: def_typ, + got: field_typ, + span_expected: def_span, + span_got: bdy_typ.meta.span, + }, + )?; + + let index = env.uf.add(PartialType::Var { + sym: enum_sym.inner, + }); + + Ok(Constrained { + meta: MetaConstrained { span, index }, + inner: ExprConstrained::Variant { + enum_sym, + variant_sym, + bdy: Box::new(bdy_typ), + }, + }) +} diff --git a/compiler/src/passes/validate/error.rs b/compiler/src/passes/validate/error.rs index 61edb28..b5c07e1 100644 --- a/compiler/src/passes/validate/error.rs +++ b/compiler/src/passes/validate/error.rs @@ -53,12 +53,23 @@ pub enum TypeError { #[label = "This should be a struct."] span: (usize, usize), }, + #[error("Tried to use variable as type.'")] + SymbolShouldBeEnum { + #[label = "This should be an enum."] + span: (usize, usize), + }, #[error("Unknown struct field.")] UnknownStructField { sym: String, #[label = "The field `{sym}` is not present in the struct definition."] span: (usize, usize), }, + #[error("Unknown enum variant.")] + UnknownEnumVariant { + sym: String, + #[label = "The variant `{sym}` is not present in the enum definition."] + span: (usize, usize), + }, #[error("Missing struct field.")] ConstructMissingField { sym: String, @@ -175,6 +186,16 @@ pub enum TypeError { span_got: (usize, usize), }, #[error("Types did not match.")] + MismatchedEnumVariant { + expect: String, + got: String, + + #[label = "Expected enum variant to have type: `{expect}`"] + span_expected: (usize, usize), + #[label = "But got this type: `{got}`"] + span_got: (usize, usize), + }, + #[error("Types did not match.")] IfExpectBool { got: String, diff --git a/compiler/src/passes/validate/resolve.rs b/compiler/src/passes/validate/resolve.rs index 68d15cc..98ebd0e 100644 --- a/compiler/src/passes/validate/resolve.rs +++ b/compiler/src/passes/validate/resolve.rs @@ -295,7 +295,11 @@ fn resolve_expr<'p>( strct: Box::new(resolve_expr(*strct, uf)?), field: field.inner, }, - Expr::Variant { .. } => todo!(), + Expr::Variant { enum_sym, variant_sym, bdy } => Expr::Variant { + enum_sym: enum_sym.inner, + variant_sym: variant_sym.inner, + bdy: Box::new(resolve_expr(*bdy, uf)?), + }, Expr::Switch { .. } => todo!(), ExprConstrained::Asm { instrs } => ExprValidated::Asm { instrs: instrs.into_iter().map(resolve_instr).collect(), diff --git a/compiler/src/passes/validate/uniquify/expr.rs b/compiler/src/passes/validate/uniquify/expr.rs index 065462e..621527a 100644 --- a/compiler/src/passes/validate/uniquify/expr.rs +++ b/compiler/src/passes/validate/uniquify/expr.rs @@ -3,7 +3,7 @@ use crate::passes::select::VarArg; use crate::passes::validate::error::TypeError; use crate::passes::validate::uniquify::r#type::uniquify_type; use crate::passes::validate::uniquify::{gen_spanned_sym, try_get}; -use crate::passes::validate::{uniquify, ExprUniquified, InstrUniquified}; +use crate::passes::validate::{ExprUniquified, InstrUniquified}; use crate::utils::push_map::PushMap; use crate::utils::unique_sym::UniqueSym; @@ -34,14 +34,14 @@ pub fn uniquify_expr<'p>( } } Expr::Var { sym } => Expr::Var { - sym: uniquify::try_get(sym, scope)?, + sym: try_get(sym, scope)?, }, Expr::Assign { sym, bnd } => Expr::Assign { - sym: uniquify::try_get(sym, scope)?, + sym: try_get(sym, scope)?, bnd: Box::new(uniquify_expr(*bnd, scope)?), }, Expr::Struct { sym, fields } => Expr::Struct { - sym: uniquify::try_get(sym, scope)?, + sym: try_get(sym, scope)?, fields: fields .into_iter() .map(|(sym, expr)| uniquify_expr(expr, scope).map(|expr| (sym, expr))) @@ -90,7 +90,15 @@ pub fn uniquify_expr<'p>( strct: Box::new(uniquify_expr(*strct, scope)?), field, }, - Expr::Variant { .. } => todo!(), + Expr::Variant { + enum_sym, + variant_sym, + bdy, + } => Expr::Variant { + enum_sym: try_get(enum_sym, scope)?, + variant_sym, + bdy: Box::new(uniquify_expr(*bdy, scope)?), + }, Expr::Switch { .. } => todo!(), ExprParsed::Asm { instrs } => ExprUniquified::Asm { instrs: instrs diff --git a/programs/good/algebraic/simple_enum_construct.sp b/programs/good/algebraic/simple_enum_construct.sp new file mode 100644 index 0000000..6bfdfee --- /dev/null +++ b/programs/good/algebraic/simple_enum_construct.sp @@ -0,0 +1,9 @@ +enum TestEnum { + Variant1: I64, + Variant2: Bool, +} + +fn main() { + let _ = TestEnum::Variant1(15); + unit +}