use std::collections::{HashMap, HashSet};
use crate::{
ast::{
self, EnumDef, Expr, ExprEnum, Mutability, Op, ParamDef, Pattern, PatternEnum, Stmt,
StmtEnum, StructDef, Type, UnaryOp, Variant, VariantExpr, VariantExprEnum,
},
env::Env,
token::{MetaInfo, SignedNumType, UnsignedNumType},
TypedExpr, TypedFnDef, TypedPattern, TypedProgram, TypedStmt, UntypedExpr, UntypedFnDef,
UntypedPattern, UntypedProgram, UntypedStmt,
};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct TypeError(pub TypeErrorEnum, pub MetaInfo);
impl PartialOrd for TypeError {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
self.1.partial_cmp(&other.1)
}
}
impl Ord for TypeError {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.1.cmp(&other.1)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum TypeErrorEnum {
NoTopLevelFn(String),
PubFnWithoutParams(String),
UnusedFn(String),
RecursiveFnDef(String),
UnknownStructOrEnum(String),
UnknownStruct(String),
UnknownStructField(String, String),
MissingStructField(String, String),
UnknownEnum(String),
UnknownEnumVariant(String, String),
UnknownIdentifier(String),
IdentifierNotDeclaredAsMutable(String),
TupleAccessOutOfBounds(usize),
DuplicateFnParam(String),
ExpectedBoolOrNumberType(Type),
ExpectedNumberType(Type),
ExpectedSignedNumberType(Type),
ExpectedArrayType(Type),
ExpectedTupleType(Type),
ExpectedStructType(Type),
ExpectedEnumType(Type),
ExpectedUnitVariantFoundTupleVariant,
ExpectedTupleVariantFoundUnitVariant,
UnexpectedEnumVariantArity {
expected: usize,
actual: usize,
},
UnexpectedType {
expected: Type,
actual: Type,
},
WrongNumberOfArgs {
expected: usize,
actual: usize,
},
TypeMismatch((Type, MetaInfo), (Type, MetaInfo)),
RangeTypeMismatch(UnsignedNumType, UnsignedNumType),
InvalidRange(u64, u64),
PatternDoesNotMatchType(Type),
PatternsAreNotExhaustive(Vec<PatternStack>),
TypeDoesNotSupportPatternMatching(Type),
}
impl std::fmt::Display for TypeErrorEnum {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
TypeErrorEnum::NoTopLevelFn(fn_name) => f.write_fmt(format_args!("'{fn_name}' is not a top level function")),
TypeErrorEnum::PubFnWithoutParams(fn_name) => f.write_fmt(format_args!("The function '{fn_name}' is declared pub, but has no parameters")),
TypeErrorEnum::UnusedFn(name) => f.write_fmt(format_args!(
"Function '{name}' is declared but never used"
)),
TypeErrorEnum::RecursiveFnDef(name) => f.write_fmt(format_args!(
"Function '{name}' is declared recursively, which is not supported"
)),
TypeErrorEnum::UnknownStructOrEnum(name) => {
f.write_fmt(format_args!("Unknown struct or enum '{name}'"))
}
TypeErrorEnum::UnknownStruct(struct_name) => {
f.write_fmt(format_args!("Unknown struct '{struct_name}'"))
}
TypeErrorEnum::UnknownStructField(struct_name, struct_field) => f.write_fmt(
format_args!("Struct '{struct_name}' does not have a field '{struct_field}'"),
),
TypeErrorEnum::MissingStructField(struct_name, struct_field) => f.write_fmt(
format_args!("Field '{struct_field}' is missing for struct '{struct_name}'"),
),
TypeErrorEnum::UnknownEnum(enum_name) => {
f.write_fmt(format_args!("Unknown enum '{enum_name}'"))
}
TypeErrorEnum::UnknownEnumVariant(enum_name, enum_variant) => f.write_fmt(
format_args!("Unknown enum variant '{enum_name}::{enum_variant}'"),
),
TypeErrorEnum::UnknownIdentifier(name) => {
f.write_fmt(format_args!("Unknown identifier '{name}'"))
}
TypeErrorEnum::IdentifierNotDeclaredAsMutable(name) => {
f.write_fmt(format_args!("'{name}' exists, but was not declared as mutable"))
}
TypeErrorEnum::TupleAccessOutOfBounds(size) => {
f.write_fmt(format_args!("The tuple only has {size} fields"))
}
TypeErrorEnum::DuplicateFnParam(name) => f.write_fmt(format_args!(
"The function parameter '{name}' is declared multiple times"
)),
TypeErrorEnum::ExpectedBoolOrNumberType(ty) => f.write_fmt(format_args!(
"Expected a boolean or number type, but found {ty}"
)),
TypeErrorEnum::ExpectedNumberType(ty) => {
f.write_fmt(format_args!("Expected a number type, but found {ty}"))
}
TypeErrorEnum::ExpectedSignedNumberType(ty) => f.write_fmt(format_args!(
"Expected a signed number type, but found {ty}"
)),
TypeErrorEnum::ExpectedArrayType(ty) => {
f.write_fmt(format_args!("Expected an array type, but found {ty}"))
}
TypeErrorEnum::ExpectedTupleType(ty) => {
f.write_fmt(format_args!("Expected a tuple type, but found {ty}"))
}
TypeErrorEnum::ExpectedStructType(ty) => {
f.write_fmt(format_args!("Expected a struct type, but found {ty}"))
}
TypeErrorEnum::ExpectedEnumType(ty) => {
f.write_fmt(format_args!("Expected an enum type, but found {ty}"))
}
TypeErrorEnum::ExpectedUnitVariantFoundTupleVariant => {
f.write_str("Expected a variant without fields, but found a tuple variant")
}
TypeErrorEnum::ExpectedTupleVariantFoundUnitVariant => {
f.write_str("Expected a tuple variant, but found a variant without fields")
}
TypeErrorEnum::UnexpectedEnumVariantArity { expected, actual } => {
f.write_fmt(format_args!(
"Expected a variant with {expected} fields, but found {actual} fields"
))
}
TypeErrorEnum::UnexpectedType { expected, actual } => {
f.write_fmt(format_args!("Expected type {expected}, but found {actual}"))
}
TypeErrorEnum::WrongNumberOfArgs { expected, actual } => {
f.write_fmt(format_args!("The function expects {expected} parameter(s), but was called with {actual} argument(s)"))
}
TypeErrorEnum::TypeMismatch((x, _), (y, _)) => f.write_fmt(format_args!(
"The operands have incompatible types; {x} vs {y}"
)),
TypeErrorEnum::RangeTypeMismatch(from, to) => f.write_fmt(format_args!(
"Start and end of range do not have the same type; {from} vs {to}"
)),
TypeErrorEnum::InvalidRange(_, _) => f.write_str("Invalid range"),
TypeErrorEnum::PatternDoesNotMatchType(ty) => {
f.write_fmt(format_args!("The pattern does not match the type {ty}"))
}
TypeErrorEnum::PatternsAreNotExhaustive(missing) => {
f.write_str("The patterns are not exhaustive. Missing cases:\n\n")?;
for pattern in missing {
f.write_str(" ")?;
let mut fields = pattern.iter();
if let Some(field) = fields.next() {
field.fmt(f)?;
}
for field in fields {
f.write_str(", ")?;
field.fmt(f)?;
}
f.write_str("\n\n")?;
}
f.write_str("...in expression")
}
TypeErrorEnum::TypeDoesNotSupportPatternMatching(ty) => {
f.write_fmt(format_args!("Type {ty} does not support pattern matching"))
}
}
}
}
type TypeErrors = Vec<Option<TypeError>>;
pub(crate) struct TopLevelTypes<'a> {
pub(crate) struct_names: HashSet<&'a String>,
pub(crate) enum_names: HashSet<&'a String>,
}
impl Type {
fn as_concrete_type(&self, types: &TopLevelTypes) -> Result<Type, TypeErrors> {
let ty = match self {
Type::Bool => Type::Bool,
Type::Unsigned(n) => Type::Unsigned(*n),
Type::Signed(n) => Type::Signed(*n),
Type::Fn(args, ret) => {
let mut concrete_args = Vec::with_capacity(args.len());
for arg in args.iter() {
concrete_args.push(arg.as_concrete_type(types)?);
}
let ret = ret.as_concrete_type(types)?;
Type::Fn(concrete_args, Box::new(ret))
}
Type::Array(elem, size) => {
let elem = elem.as_concrete_type(types)?;
Type::Array(Box::new(elem), *size)
}
Type::Tuple(fields) => {
let mut concrete_fields = Vec::with_capacity(fields.len());
for field in fields.iter() {
concrete_fields.push(field.as_concrete_type(types)?);
}
Type::Tuple(concrete_fields)
}
Type::UntypedTopLevelDefinition(name, meta) => {
if types.struct_names.contains(name) {
Type::Struct(name.clone())
} else if types.enum_names.contains(name) {
Type::Enum(name.clone())
} else {
let e = TypeErrorEnum::UnknownStructOrEnum(name.clone());
return Err(vec![Some(TypeError(e, *meta))]);
}
}
Type::Struct(name) => Type::Struct(name.clone()),
Type::Enum(name) => Type::Enum(name.clone()),
};
Ok(ty)
}
}
pub struct Defs<'a> {
structs: HashMap<&'a str, (Vec<&'a str>, HashMap<&'a str, Type>)>,
enums: HashMap<&'a str, HashMap<&'a str, Option<Vec<Type>>>>,
fns: HashMap<&'a str, &'a UntypedFnDef>,
}
impl<'a> Defs<'a> {
pub(crate) fn new(
struct_defs: &'a HashMap<String, StructDef>,
enum_defs: &'a HashMap<String, EnumDef>,
) -> Self {
let mut defs = Self {
structs: HashMap::new(),
enums: HashMap::new(),
fns: HashMap::new(),
};
for (struct_name, struct_def) in struct_defs.iter() {
let mut field_names = Vec::with_capacity(struct_def.fields.len());
let mut field_types = HashMap::with_capacity(struct_def.fields.len());
for (field_name, field_type) in &struct_def.fields {
field_names.push(field_name.as_str());
field_types.insert(field_name.as_str(), field_type.clone());
}
defs.structs.insert(struct_name, (field_names, field_types));
}
for (enum_name, enum_def) in enum_defs.iter() {
let mut enum_variants = HashMap::new();
for variant in &enum_def.variants {
enum_variants.insert(variant.variant_name(), variant.types());
}
defs.enums.insert(enum_name, enum_variants);
}
defs
}
}
pub(crate) struct TypedFns {
currently_being_checked: HashSet<String>,
typed: HashMap<String, Result<TypedFnDef, TypeErrors>>,
}
impl TypedFns {
pub(crate) fn new() -> Self {
Self {
currently_being_checked: HashSet::new(),
typed: HashMap::new(),
}
}
}
impl UntypedProgram {
pub fn type_check(&self) -> Result<TypedProgram, Vec<TypeError>> {
let mut errors = vec![];
let mut struct_names = HashSet::with_capacity(self.struct_defs.len());
let mut enum_names = HashSet::with_capacity(self.enum_defs.len());
struct_names.extend(self.struct_defs.keys());
enum_names.extend(self.enum_defs.keys());
let top_level_defs = TopLevelTypes {
struct_names,
enum_names,
};
let mut struct_defs = HashMap::with_capacity(self.struct_defs.len());
for (struct_name, struct_def) in self.struct_defs.iter() {
let meta = struct_def.meta;
let mut fields = Vec::with_capacity(struct_def.fields.len());
for (name, ty) in struct_def.fields.iter() {
match ty.as_concrete_type(&top_level_defs) {
Ok(ty) => fields.push((name.clone(), ty)),
Err(e) => errors.extend(e),
}
}
struct_defs.insert(struct_name.clone(), StructDef { fields, meta });
}
let mut enum_defs = HashMap::with_capacity(self.enum_defs.len());
for (enum_name, enum_def) in self.enum_defs.iter() {
let meta = enum_def.meta;
let mut variants = Vec::with_capacity(enum_def.variants.len());
for variant in enum_def.variants.iter() {
variants.push(match variant {
Variant::Unit(variant_name) => Variant::Unit(variant_name.clone()),
Variant::Tuple(variant_name, variant_fields) => {
let mut fields = Vec::with_capacity(variant_fields.len());
for field in variant_fields.iter() {
match field.as_concrete_type(&top_level_defs) {
Ok(field) => fields.push(field),
Err(e) => errors.extend(e),
}
}
Variant::Tuple(variant_name.clone(), fields)
}
});
}
enum_defs.insert(enum_name.clone(), EnumDef { variants, meta });
}
let mut untyped_defs = Defs::new(&struct_defs, &enum_defs);
let mut checked_fn_defs = TypedFns::new();
for (fn_name, fn_def) in self.fn_defs.iter() {
untyped_defs.fns.insert(fn_name, fn_def);
}
for (fn_name, fn_def) in self.fn_defs.iter() {
if fn_def.is_pub {
if fn_def.params.is_empty() {
let e = TypeErrorEnum::PubFnWithoutParams(fn_name.clone());
errors.push(Some(TypeError(e, fn_def.meta)));
} else {
let typed_fn =
fn_def.type_check(&top_level_defs, &mut checked_fn_defs, &untyped_defs);
if let Err(e) = typed_fn.clone() {
errors.extend(e);
}
checked_fn_defs.typed.insert(fn_name.clone(), typed_fn);
}
}
}
for (fn_name, fn_def) in self.fn_defs.iter() {
if !fn_def.is_pub && !checked_fn_defs.typed.contains_key(fn_name.as_str()) {
let e = TypeErrorEnum::UnusedFn(fn_name.to_string());
errors.push(Some(TypeError(e, fn_def.meta)));
}
}
let mut fn_defs = HashMap::new();
for (fn_name, fn_def) in checked_fn_defs.typed.into_iter() {
if let Ok(fn_def) = fn_def {
fn_defs.insert(fn_name, fn_def);
}
}
if errors.is_empty() {
Ok(TypedProgram {
struct_defs,
enum_defs,
fn_defs,
})
} else {
let mut errors: Vec<TypeError> = errors.into_iter().flatten().collect();
errors.sort();
Err(errors)
}
}
}
impl UntypedFnDef {
fn type_check(
&self,
top_level_defs: &TopLevelTypes,
fns: &mut TypedFns,
defs: &Defs,
) -> Result<TypedFnDef, TypeErrors> {
if fns.currently_being_checked.contains(&self.identifier) {
let e = TypeErrorEnum::RecursiveFnDef(self.identifier.clone());
return Err(vec![Some(TypeError(e, self.meta))]);
} else {
fns.currently_being_checked.insert(self.identifier.clone());
}
let mut errors = vec![];
let mut env = Env::new();
env.push();
let mut params = Vec::with_capacity(self.params.len());
let mut param_identifiers = HashSet::new();
for param in self.params.iter() {
let ParamDef(mutability, identifier, ty) = param;
if param_identifiers.contains(identifier) {
let e = TypeErrorEnum::DuplicateFnParam(identifier.clone());
errors.push(Some(TypeError(e, self.meta)));
} else {
param_identifiers.insert(identifier);
}
match ty.as_concrete_type(top_level_defs) {
Ok(ty) => {
env.let_in_current_scope(identifier.clone(), (Some(ty.clone()), *mutability));
params.push(ParamDef(*mutability, identifier.clone(), ty));
}
Err(e) => {
env.let_in_current_scope(identifier.clone(), (None, *mutability));
errors.extend(e);
}
}
}
let body = type_check_block(&self.body, self.meta, top_level_defs, &mut env, fns, defs);
fns.currently_being_checked.remove(&self.identifier);
env.pop();
match body {
Ok((body, mut ret_expr)) => match self.ty.as_concrete_type(top_level_defs) {
Ok(ret_ty) => {
if let Err(e) = check_type(&mut ret_expr, &ret_ty) {
errors.extend(e);
}
if errors.is_empty() {
Ok(TypedFnDef {
is_pub: self.is_pub,
identifier: self.identifier.clone(),
params,
ty: ret_ty,
body,
meta: self.meta,
})
} else {
Err(errors)
}
}
Err(e) => {
errors.extend(e);
Err(errors)
}
},
Err(e) => {
errors.extend(e);
Err(errors)
}
}
}
}
fn type_check_block(
block: &[UntypedStmt],
meta: MetaInfo,
top_level_defs: &TopLevelTypes,
env: &mut Env<(Option<Type>, Mutability)>,
fns: &mut TypedFns,
defs: &Defs,
) -> Result<(Vec<TypedStmt>, TypedExpr), TypeErrors> {
let mut typed_block = Vec::with_capacity(block.len());
let mut ret_expr = Expr::typed(ExprEnum::TupleLiteral(vec![]), Type::Tuple(vec![]), meta);
let mut errors = vec![];
for (i, stmt) in block.iter().enumerate() {
match stmt.type_check(top_level_defs, env, fns, defs) {
Ok(stmt) => {
if i == block.len() - 1 {
if let Stmt(StmtEnum::Expr(expr), _) = &stmt {
ret_expr = expr.clone();
}
}
typed_block.push(stmt);
}
Err(e) => {
errors.extend(e);
}
}
}
if errors.is_empty() {
Ok((typed_block, ret_expr))
} else {
Err(errors)
}
}
impl UntypedStmt {
pub(crate) fn type_check(
&self,
top_level_defs: &TopLevelTypes,
env: &mut Env<(Option<Type>, Mutability)>,
fns: &mut TypedFns,
defs: &Defs,
) -> Result<TypedStmt, TypeErrors> {
let meta = self.1;
match &self.0 {
ast::StmtEnum::Let(pattern, binding) => {
match binding.type_check(top_level_defs, env, fns, defs) {
Ok(binding) => {
let pattern =
pattern.type_check(env, fns, defs, Some(binding.2.clone()))?;
Ok(Stmt(StmtEnum::Let(pattern, binding), meta))
}
Err(mut errors) => {
if let Err(e) = pattern.type_check(env, fns, defs, None) {
errors.extend(e);
}
Err(errors)
}
}
}
ast::StmtEnum::LetMut(identifier, binding) => {
match binding.type_check(top_level_defs, env, fns, defs) {
Ok(binding) => {
env.let_in_current_scope(
identifier.clone(),
(Some(binding.2.clone()), Mutability::Mutable),
);
Ok(Stmt(StmtEnum::LetMut(identifier.clone(), binding), meta))
}
Err(e) => {
env.let_in_current_scope(identifier.clone(), (None, Mutability::Mutable));
Err(e)
}
}
}
ast::StmtEnum::Expr(expr) => {
let expr = expr.type_check(top_level_defs, env, fns, defs)?;
Ok(Stmt(StmtEnum::Expr(expr), meta))
}
ast::StmtEnum::VarAssign(identifier, value) => {
match env.get(identifier) {
Some((Some(ty), Mutability::Mutable)) => {
let mut value = value.type_check(top_level_defs, env, fns, defs)?;
check_type(&mut value, &ty)?;
Ok(Stmt(StmtEnum::VarAssign(identifier.clone(), value), meta))
}
Some((None, Mutability::Mutable)) => {
Err(vec![None])
}
Some((_, Mutability::Immutable)) => Err(vec![Some(TypeError(
TypeErrorEnum::IdentifierNotDeclaredAsMutable(identifier.clone()),
meta,
))]),
None => Err(vec![Some(TypeError(
TypeErrorEnum::UnknownIdentifier(identifier.clone()),
meta,
))]),
}
}
ast::StmtEnum::ArrayAssign(identifier, index, value) => {
match env.get(identifier) {
Some((Some(array_ty), Mutability::Mutable)) => {
let (elem_ty, _) = expect_array_type(&array_ty, meta)?;
let mut index = index.type_check(top_level_defs, env, fns, defs)?;
check_type(&mut index, &Type::Unsigned(UnsignedNumType::Usize))?;
let mut value = value.type_check(top_level_defs, env, fns, defs)?;
check_type(&mut value, &elem_ty)?;
Ok(Stmt(
StmtEnum::ArrayAssign(identifier.clone(), index, value),
meta,
))
}
Some((None, Mutability::Mutable)) => {
Err(vec![None])
}
Some((_, Mutability::Immutable)) => Err(vec![Some(TypeError(
TypeErrorEnum::IdentifierNotDeclaredAsMutable(identifier.clone()),
meta,
))]),
None => Err(vec![Some(TypeError(
TypeErrorEnum::UnknownIdentifier(identifier.clone()),
meta,
))]),
}
}
ast::StmtEnum::ForEachLoop(var, binding, body) => {
let binding = binding.type_check(top_level_defs, env, fns, defs)?;
let (elem_ty, _) = expect_array_type(&binding.2, meta)?;
let mut body_typed = Vec::with_capacity(body.len());
env.push();
env.let_in_current_scope(var.clone(), (Some(elem_ty), Mutability::Immutable));
for stmt in body {
body_typed.push(stmt.type_check(top_level_defs, env, fns, defs)?);
}
env.pop();
Ok(Stmt(
StmtEnum::ForEachLoop(var.clone(), binding, body_typed),
meta,
))
}
}
}
}
impl UntypedExpr {
pub(crate) fn type_check(
&self,
top_level_defs: &TopLevelTypes,
env: &mut Env<(Option<Type>, Mutability)>,
fns: &mut TypedFns,
defs: &Defs,
) -> Result<TypedExpr, TypeErrors> {
let Expr(expr, meta, _) = self;
let meta = *meta;
let (expr, ty) = match expr {
ExprEnum::True => (ExprEnum::True, Type::Bool),
ExprEnum::False => (ExprEnum::False, Type::Bool),
ExprEnum::NumUnsigned(n, type_suffix) => (
ExprEnum::NumUnsigned(*n, *type_suffix),
Type::Unsigned(*type_suffix),
),
ExprEnum::NumSigned(n, type_suffix) => (
ExprEnum::NumSigned(*n, *type_suffix),
Type::Signed(*type_suffix),
),
ExprEnum::Identifier(identifier) => match env.get(identifier) {
Some((Some(ty), _mutability)) => (ExprEnum::Identifier(identifier.clone()), ty),
Some((None, _mutability)) => {
return Err(vec![None]);
}
None => {
return Err(vec![Some(TypeError(
TypeErrorEnum::UnknownIdentifier(identifier.clone()),
meta,
))]);
}
},
ExprEnum::ArrayLiteral(fields) => {
let mut errors = vec![];
let array_size = fields.len();
let mut fields = fields.iter();
let first_field =
fields
.next()
.unwrap()
.type_check(top_level_defs, env, fns, defs)?;
let first_meta = first_field.1;
let first_ty = first_field.2.clone();
let mut typed_fields = vec![first_field];
for field in fields {
match field.type_check(top_level_defs, env, fns, defs) {
Ok(field) => {
if field.2 != first_ty {
let e = TypeErrorEnum::TypeMismatch(
(first_ty.clone(), first_meta),
(field.2.clone(), field.1),
);
errors.push(Some(TypeError(e, field.1)));
}
typed_fields.push(field);
}
Err(e) => errors.extend(e),
}
}
if errors.is_empty() {
let ty = Type::Array(Box::new(first_ty), array_size);
(ExprEnum::ArrayLiteral(typed_fields), ty)
} else {
return Err(errors);
}
}
ExprEnum::ArrayRepeatLiteral(value, size) => {
let value = value.type_check(top_level_defs, env, fns, defs)?;
let ty = Type::Array(Box::new(value.2.clone()), *size);
(ExprEnum::ArrayRepeatLiteral(Box::new(value), *size), ty)
}
ExprEnum::ArrayAccess(arr, index) => {
let arr = arr.type_check(top_level_defs, env, fns, defs)?;
let mut index = index.type_check(top_level_defs, env, fns, defs)?;
let Expr(_, array_meta, array_ty) = &arr;
let (elem_ty, _) = expect_array_type(array_ty, *array_meta)?;
check_type(&mut index, &Type::Unsigned(UnsignedNumType::Usize))?;
(
ExprEnum::ArrayAccess(Box::new(arr), Box::new(index)),
elem_ty,
)
}
ExprEnum::TupleLiteral(values) => {
let mut errors = vec![];
let mut typed_values = Vec::with_capacity(values.len());
let mut types = Vec::with_capacity(values.len());
for v in values {
match v.type_check(top_level_defs, env, fns, defs) {
Ok(typed) => {
types.push(typed.2.clone());
typed_values.push(typed);
}
Err(e) => {
errors.extend(e);
}
}
}
if errors.is_empty() {
let ty = Type::Tuple(types);
(ExprEnum::TupleLiteral(typed_values), ty)
} else {
return Err(errors);
}
}
ExprEnum::TupleAccess(tuple, index) => {
let tuple = tuple.type_check(top_level_defs, env, fns, defs)?;
let Expr(_, meta, ty) = &tuple;
let value_types = expect_tuple_type(ty, *meta)?;
if *index < value_types.len() {
let ty = value_types[*index].clone();
(ExprEnum::TupleAccess(Box::new(tuple), *index), ty)
} else {
let e = TypeErrorEnum::TupleAccessOutOfBounds(value_types.len());
return Err(vec![Some(TypeError(e, *meta))]);
}
}
ExprEnum::UnaryOp(UnaryOp::Neg, x) => {
let x = x.type_check(top_level_defs, env, fns, defs)?;
let ty = x.2.clone();
expect_signed_num_type(&ty, x.1)?;
(ExprEnum::UnaryOp(UnaryOp::Neg, Box::new(x)), ty)
}
ExprEnum::UnaryOp(UnaryOp::Not, x) => {
let x = x.type_check(top_level_defs, env, fns, defs)?;
let ty = x.2.clone();
expect_bool_or_num_type(&ty, x.1)?;
(ExprEnum::UnaryOp(UnaryOp::Not, Box::new(x)), ty)
}
ExprEnum::Op(op, x, y) => match op {
Op::Add | Op::Sub | Op::Mul | Op::Div | Op::Mod => {
let mut x = x.type_check(top_level_defs, env, fns, defs)?;
let mut y = y.type_check(top_level_defs, env, fns, defs)?;
let ty = unify(&mut x, &mut y, meta)?;
expect_num_type(&ty, meta)?;
(ExprEnum::Op(*op, Box::new(x), Box::new(y)), ty)
}
Op::ShortCircuitAnd | Op::ShortCircuitOr => {
let x = x.type_check(top_level_defs, env, fns, defs)?;
let y = y.type_check(top_level_defs, env, fns, defs)?;
for (meta, ty) in [(&x.1, &x.2), (&y.1, &y.2)] {
match ty {
Type::Bool => {}
ty => {
return Err(vec![Some(TypeError(
TypeErrorEnum::UnexpectedType {
expected: Type::Bool,
actual: ty.clone(),
},
*meta,
))])
}
}
}
(ExprEnum::Op(*op, Box::new(x), Box::new(y)), Type::Bool)
}
Op::BitAnd | Op::BitXor | Op::BitOr => {
let mut x = x.type_check(top_level_defs, env, fns, defs)?;
let mut y = y.type_check(top_level_defs, env, fns, defs)?;
let ty = unify(&mut x, &mut y, meta)?;
expect_bool_or_num_type(&ty, meta)?;
(ExprEnum::Op(*op, Box::new(x), Box::new(y)), ty)
}
Op::GreaterThan | Op::LessThan => {
let mut x = x.type_check(top_level_defs, env, fns, defs)?;
let mut y = y.type_check(top_level_defs, env, fns, defs)?;
let ty = unify(&mut x, &mut y, meta)?;
expect_num_type(&ty, meta)?;
(ExprEnum::Op(*op, Box::new(x), Box::new(y)), Type::Bool)
}
Op::Eq | Op::NotEq => {
let mut x = x.type_check(top_level_defs, env, fns, defs)?;
let mut y = y.type_check(top_level_defs, env, fns, defs)?;
unify(&mut x, &mut y, meta)?;
let expr = ExprEnum::Op(*op, Box::new(x), Box::new(y));
(expr, Type::Bool)
}
Op::ShiftLeft | Op::ShiftRight => {
let x = x.type_check(top_level_defs, env, fns, defs)?;
let mut y = y.type_check(top_level_defs, env, fns, defs)?;
let Expr(_, meta_x, ty_x) = x.clone();
expect_num_type(&ty_x, meta_x)?;
check_type(&mut y, &Type::Unsigned(UnsignedNumType::U8))?;
(ExprEnum::Op(*op, Box::new(x), Box::new(y)), ty_x)
}
},
ExprEnum::Block(stmts) => {
env.push();
let (body, ret_expr) =
type_check_block(stmts, meta, top_level_defs, env, fns, defs)?;
let ty = ret_expr.2;
env.pop();
(ExprEnum::Block(body), ty)
}
ExprEnum::FnCall(identifier, args) => {
let mut errors = vec![];
if !fns.typed.contains_key(identifier) {
if let Some(fn_def) = defs.fns.get(identifier.as_str()) {
let fn_def = fn_def.type_check(top_level_defs, fns, defs);
fns.typed.insert(identifier.clone(), fn_def.clone());
if let Err(e) = fn_def {
errors.extend(e);
}
}
}
match (fns.typed.get(identifier), env.get(identifier)) {
(Some(Ok(fn_def)), None) => {
let ret_ty = fn_def.ty.clone();
let mut fn_arg_types = Vec::with_capacity(fn_def.params.len());
for ParamDef(_, _, ty) in fn_def.params.iter() {
fn_arg_types.push(ty.clone());
}
let mut arg_types = Vec::with_capacity(args.len());
let mut arg_meta = Vec::with_capacity(args.len());
let mut arg_exprs = Vec::with_capacity(args.len());
for arg in args.iter() {
match arg.type_check(top_level_defs, env, fns, defs) {
Ok(arg) => {
let Expr(_, meta, ty) = &arg;
arg_types.push(ty.clone());
arg_meta.push(*meta);
arg_exprs.push(arg);
}
Err(e) => errors.extend(e),
}
}
if errors.is_empty() {
if fn_arg_types.len() != arg_types.len() {
let e = TypeErrorEnum::WrongNumberOfArgs {
expected: fn_arg_types.len(),
actual: arg_types.len(),
};
errors.push(Some(TypeError(e, meta)));
}
for (expected, actual) in fn_arg_types.into_iter().zip(&mut arg_exprs) {
if let Err(e) = check_type(actual, &expected) {
errors.extend(e);
}
}
}
if errors.is_empty() {
let expr = ExprEnum::FnCall(identifier.clone(), arg_exprs);
(expr, ret_ty)
} else {
return Err(errors);
}
}
(Some(Err(_)), None) => {
errors.push(None);
return Err(errors);
}
(None, _) => {
let e = TypeErrorEnum::UnknownIdentifier(identifier.clone());
errors.push(Some(TypeError(e, meta)));
return Err(errors);
}
(_, Some(_)) => {
let e = TypeErrorEnum::NoTopLevelFn(identifier.clone());
errors.push(Some(TypeError(e, meta)));
return Err(errors);
}
}
}
ExprEnum::If(condition, case_true, case_false) => {
let condition = condition.type_check(top_level_defs, env, fns, defs);
let case_true = case_true.type_check(top_level_defs, env, fns, defs);
let case_false = case_false.type_check(top_level_defs, env, fns, defs);
match (condition, case_true, case_false) {
(Ok(mut condition), Ok(mut case_true), Ok(mut case_false)) => {
check_type(&mut condition, &Type::Bool)?;
let ty = unify(&mut case_true, &mut case_false, meta)?;
let expr = ExprEnum::If(
Box::new(condition),
Box::new(case_true),
Box::new(case_false),
);
(expr, ty)
}
(condition, case_true, case_false) => {
let mut errors = vec![];
if let Err(e) = condition {
errors.extend(e);
}
if let Err(e) = case_true {
errors.extend(e);
}
if let Err(e) = case_false {
errors.extend(e);
}
return Err(errors);
}
}
}
ExprEnum::Cast(ty, expr) => {
let ty = ty.as_concrete_type(top_level_defs)?;
let expr = expr.type_check(top_level_defs, env, fns, defs)?;
let Expr(_, _, expr_ty) = &expr;
expect_bool_or_num_type(expr_ty, meta)?;
expect_bool_or_num_type(&ty, meta)?;
(ExprEnum::Cast(ty.clone(), Box::new(expr)), ty)
}
ExprEnum::Range((from, from_suffix), (to, to_suffix)) => {
if from >= to || (to - from) > u32::MAX as u64 {
let e = TypeErrorEnum::InvalidRange(*from, *to);
return Err(vec![Some(TypeError(e, meta))]);
}
if from_suffix != to_suffix {
let e = TypeErrorEnum::RangeTypeMismatch(*from_suffix, *to_suffix);
return Err(vec![Some(TypeError(e, meta))]);
}
let ty = Type::Array(Box::new(Type::Unsigned(*from_suffix)), (to - from) as usize);
(
ExprEnum::Range((*from, *from_suffix), (*to, *to_suffix)),
ty,
)
}
ExprEnum::EnumLiteral(identifier, variant) => {
if let Some(enum_def) = defs.enums.get(identifier.as_str()) {
let VariantExpr(variant_name, variant, meta) = variant.as_ref();
let meta = *meta;
if let Some(types) = enum_def.get(variant_name.as_str()) {
match (variant, types) {
(VariantExprEnum::Unit, None) => {
let variant = VariantExpr(
variant_name.to_string(),
VariantExprEnum::Unit,
meta,
);
let ty = Type::Enum(identifier.clone());
(
ExprEnum::EnumLiteral(identifier.clone(), Box::new(variant)),
ty,
)
}
(VariantExprEnum::Tuple(values), Some(types)) => {
if values.len() != types.len() {
let e = TypeErrorEnum::UnexpectedEnumVariantArity {
expected: types.len(),
actual: values.len(),
};
return Err(vec![Some(TypeError(e, meta))]);
}
let mut errors = vec![];
let mut exprs = Vec::with_capacity(values.len());
for (v, ty) in values.iter().zip(types) {
match v.type_check(top_level_defs, env, fns, defs) {
Ok(mut expr) => {
if let Err(e) = check_type(&mut expr, ty) {
errors.extend(e);
}
exprs.push(expr);
}
Err(e) => errors.extend(e),
}
}
let variant = VariantExpr(
variant_name.to_string(),
VariantExprEnum::Tuple(exprs),
meta,
);
let ty = Type::Enum(identifier.clone());
if errors.is_empty() {
(
ExprEnum::EnumLiteral(
identifier.clone(),
Box::new(variant),
),
ty,
)
} else {
return Err(errors);
}
}
(VariantExprEnum::Unit, Some(_)) => {
let e = TypeErrorEnum::ExpectedTupleVariantFoundUnitVariant;
return Err(vec![Some(TypeError(e, meta))]);
}
(VariantExprEnum::Tuple(_), None) => {
let e = TypeErrorEnum::ExpectedUnitVariantFoundTupleVariant;
return Err(vec![Some(TypeError(e, meta))]);
}
}
} else {
let e = TypeErrorEnum::UnknownEnumVariant(
identifier.clone(),
variant_name.to_string(),
);
return Err(vec![Some(TypeError(e, meta))]);
}
} else {
let e = TypeErrorEnum::UnknownEnum(identifier.clone());
return Err(vec![Some(TypeError(e, meta))]);
}
}
ExprEnum::Match(expr, clauses) => {
let expr = expr.type_check(top_level_defs, env, fns, defs)?;
let ty = &expr.2;
match ty {
Type::Bool
| Type::Unsigned(_)
| Type::Signed(_)
| Type::Tuple(_)
| Type::Struct(_)
| Type::Enum(_) => {}
Type::Fn(_, _) | Type::Array(_, _) => {
let e = TypeErrorEnum::TypeDoesNotSupportPatternMatching(ty.clone());
return Err(vec![Some(TypeError(e, meta))]);
}
Type::UntypedTopLevelDefinition(_, _) => unreachable!(
"Untyped top level types should have been typechecked at this point"
),
}
let mut errors = vec![];
let mut typed_clauses = Vec::with_capacity(clauses.len());
for (pattern, expr) in clauses {
env.push();
let pattern = pattern.type_check(env, fns, defs, Some(ty.clone()));
let expr = expr.type_check(top_level_defs, env, fns, defs);
env.pop();
match (pattern, expr) {
(Ok(pattern), Ok(expr)) => typed_clauses.push((pattern, expr)),
(Ok(_), Err(e)) => errors.extend(e),
(Err(e), Ok(_)) => errors.extend(e),
(Err(e1), Err(e2)) => {
errors.extend(e1);
errors.extend(e2);
}
}
}
let ret_ty = {
let (_, Expr(_, _, ret_ty)) = &typed_clauses.get(0).unwrap();
for (_, expr) in typed_clauses.iter() {
let Expr(_, meta, ty) = expr;
if ret_ty != ty {
let e = TypeErrorEnum::UnexpectedType {
expected: ret_ty.clone(),
actual: ty.clone(),
};
errors.push(Some(TypeError(e, *meta)));
}
}
ret_ty.clone()
};
let patterns: Vec<_> = typed_clauses.iter().map(|(p, _)| p).collect();
if let Err(e) = check_exhaustiveness(patterns.as_slice(), ty, defs, meta) {
errors.push(Some(e));
}
if errors.is_empty() {
(ExprEnum::Match(Box::new(expr), typed_clauses), ret_ty)
} else {
return Err(errors);
}
}
ExprEnum::StructLiteral(name, fields) => {
if let Some((_, struct_def)) = defs.structs.get(name.as_str()) {
let mut errors = vec![];
let mut typed_fields = Vec::with_capacity(fields.len());
for (field_name, field_value) in fields {
if let Some(expected_type) = struct_def.get(field_name.as_str()) {
match field_value.type_check(top_level_defs, env, fns, defs) {
Ok(mut typed_field) => {
if let Err(e) = check_type(&mut typed_field, expected_type) {
errors.extend(e);
}
typed_fields.push((field_name.clone(), typed_field));
}
Err(e) => errors.extend(e),
}
} else {
let e =
TypeErrorEnum::UnknownStructField(name.clone(), field_name.clone());
errors.push(Some(TypeError(e, meta)));
}
}
if struct_def.len() > fields.len() {
for expected_field_name in struct_def.keys() {
if !fields.iter().any(|(f, _)| f == expected_field_name) {
let e = TypeErrorEnum::MissingStructField(
name.clone(),
expected_field_name.to_string(),
);
errors.push(Some(TypeError(e, meta)));
}
}
}
if errors.is_empty() {
(
ExprEnum::StructLiteral(name.clone(), typed_fields),
Type::Struct(name.clone()),
)
} else {
return Err(errors);
}
} else {
let e = TypeErrorEnum::UnknownStruct(name.clone());
return Err(vec![Some(TypeError(e, meta))]);
}
}
ExprEnum::StructAccess(struct_expr, field) => {
let struct_expr = struct_expr.type_check(top_level_defs, env, fns, defs)?;
let name = expect_struct_type(&struct_expr.2, struct_expr.1)?;
if let Some((_, struct_def)) = defs.structs.get(name.as_str()) {
if let Some(field_ty) = struct_def.get(field.as_str()) {
(
ExprEnum::StructAccess(Box::new(struct_expr), field.clone()),
field_ty.clone(),
)
} else {
let e = TypeErrorEnum::UnknownStructField(name.clone(), field.clone());
return Err(vec![Some(TypeError(e, meta))]);
}
} else {
let e = TypeErrorEnum::UnknownStruct(name.clone());
return Err(vec![Some(TypeError(e, meta))]);
}
}
};
Ok(Expr::typed(expr, ty, meta))
}
}
impl UntypedPattern {
fn type_check(
&self,
env: &mut Env<(Option<Type>, Mutability)>,
fns: &mut TypedFns,
defs: &Defs,
ty: Option<Type>,
) -> Result<TypedPattern, TypeErrors> {
let Pattern(pattern, meta, _) = self;
let meta = *meta;
let pattern = match pattern {
PatternEnum::Identifier(s) => {
env.let_in_current_scope(s.clone(), (ty.clone(), Mutability::Immutable));
PatternEnum::Identifier(s.clone())
}
PatternEnum::True => match &ty {
Some(Type::Bool) => PatternEnum::True,
Some(ty) => {
let e = TypeErrorEnum::UnexpectedType {
expected: Type::Bool,
actual: ty.clone(),
};
return Err(vec![Some(TypeError(e, meta))]);
}
None => {
return Err(vec![None]);
}
},
PatternEnum::False => match &ty {
Some(Type::Bool) => PatternEnum::False,
Some(ty) => {
let e = TypeErrorEnum::UnexpectedType {
expected: Type::Bool,
actual: ty.clone(),
};
return Err(vec![Some(TypeError(e, meta))]);
}
None => {
return Err(vec![None]);
}
},
PatternEnum::NumUnsigned(n, suffix) => {
if let Some(ty) = &ty {
expect_num_type(ty, meta)?;
PatternEnum::NumUnsigned(*n, *suffix)
} else {
return Err(vec![None]);
}
}
PatternEnum::NumSigned(n, suffix) => {
if let Some(ty) = &ty {
expect_signed_num_type(ty, meta)?;
PatternEnum::NumSigned(*n, *suffix)
} else {
return Err(vec![None]);
}
}
PatternEnum::UnsignedInclusiveRange(from, to, suffix) => {
if let Some(ty) = &ty {
expect_num_type(ty, meta)?;
PatternEnum::UnsignedInclusiveRange(*from, *to, *suffix)
} else {
return Err(vec![None]);
}
}
PatternEnum::SignedInclusiveRange(from, to, suffix) => {
if let Some(ty) = &ty {
expect_signed_num_type(ty, meta)?;
PatternEnum::SignedInclusiveRange(*from, *to, *suffix)
} else {
return Err(vec![None]);
}
}
PatternEnum::Tuple(fields) => {
if let Some(ty) = &ty {
let field_types = expect_tuple_type(ty, meta)?;
if field_types.len() != fields.len() {
let e = TypeErrorEnum::UnexpectedEnumVariantArity {
expected: field_types.len(),
actual: fields.len(),
};
return Err(vec![Some(TypeError(e, meta))]);
}
let mut errors = vec![];
let mut typed_fields = Vec::with_capacity(fields.len());
for (field, ty) in fields.iter().zip(field_types) {
match field.type_check(env, fns, defs, Some(ty)) {
Ok(typed_field) => typed_fields.push(typed_field),
Err(e) => errors.extend(e),
}
}
if errors.is_empty() {
PatternEnum::Tuple(typed_fields)
} else {
return Err(errors);
}
} else {
let mut errors = vec![None];
for field in fields.iter() {
match field.type_check(env, fns, defs, ty.clone()) {
Ok(_) => {}
Err(e) => errors.extend(e),
}
}
return Err(errors);
}
}
PatternEnum::Struct(struct_name, fields)
| PatternEnum::StructIgnoreRemaining(struct_name, fields) => {
let ignore_remaining_fields =
matches!(pattern, PatternEnum::StructIgnoreRemaining(_, _));
if let Some(ty) = &ty {
let struct_def_name = expect_struct_type(ty, meta)?;
if &struct_def_name != struct_name {
let e = TypeErrorEnum::UnexpectedType {
expected: ty.clone(),
actual: Type::Struct(struct_name.clone()),
};
return Err(vec![Some(TypeError(e, meta))]);
}
}
if let Some((_, struct_def)) = defs.structs.get(struct_name.as_str()) {
let mut errors = vec![];
let mut typed_fields = Vec::with_capacity(fields.len());
for (field_name, field_value) in fields {
if let Some(field_type) = struct_def.get(field_name.as_str()) {
match field_value.type_check(env, fns, defs, Some(field_type.clone())) {
Ok(typed_field) => {
typed_fields.push((field_name.clone(), typed_field))
}
Err(e) => errors.extend(e),
}
} else {
let e = TypeErrorEnum::UnknownStructField(
struct_name.clone(),
field_name.clone(),
);
errors.push(Some(TypeError(e, meta)));
}
}
if !ignore_remaining_fields && struct_def.len() > fields.len() {
for expected_field_name in struct_def.keys() {
if !fields.iter().any(|(f, _)| f == expected_field_name) {
let e = TypeErrorEnum::MissingStructField(
struct_name.clone(),
expected_field_name.to_string(),
);
errors.push(Some(TypeError(e, meta)));
}
}
}
if errors.is_empty() {
PatternEnum::Struct(struct_name.clone(), typed_fields)
} else {
return Err(errors);
}
} else {
let e = TypeErrorEnum::UnknownStruct(struct_name.clone());
return Err(vec![Some(TypeError(e, meta))]);
}
}
PatternEnum::EnumUnit(enum_name, variant_name)
| PatternEnum::EnumTuple(enum_name, variant_name, _) => {
if let Some(ty) = &ty {
match &ty {
Type::Enum(enum_def_name) if enum_def_name == enum_name => {}
_ => {
let e = TypeErrorEnum::UnexpectedType {
expected: Type::Enum(enum_name.clone()),
actual: ty.clone(),
};
return Err(vec![Some(TypeError(e, meta))]);
}
}
}
if let Some(enum_def) = defs.enums.get(enum_name.as_str()) {
if let Some(variant) = enum_def.get(variant_name.as_str()) {
match (pattern, variant) {
(PatternEnum::EnumUnit(_, _), None) => {
PatternEnum::EnumUnit(enum_name.clone(), variant_name.clone())
}
(PatternEnum::EnumTuple(_, _, fields), Some(field_types)) => {
if field_types.len() != fields.len() {
let e = TypeErrorEnum::UnexpectedEnumVariantArity {
expected: field_types.len(),
actual: fields.len(),
};
return Err(vec![Some(TypeError(e, meta))]);
}
let mut errors = vec![];
let mut typed_fields = Vec::with_capacity(fields.len());
for (field, ty) in fields.iter().zip(field_types) {
match field.type_check(env, fns, defs, Some(ty.clone())) {
Ok(typed_field) => typed_fields.push(typed_field),
Err(e) => errors.extend(e),
}
}
if errors.is_empty() {
PatternEnum::EnumTuple(
enum_name.clone(),
variant_name.clone(),
typed_fields,
)
} else {
return Err(errors);
}
}
(PatternEnum::EnumUnit(_, _), Some(_)) => {
let e = TypeErrorEnum::ExpectedTupleVariantFoundUnitVariant;
return Err(vec![Some(TypeError(e, meta))]);
}
(PatternEnum::EnumTuple(_, _, _), None) => {
let e = TypeErrorEnum::ExpectedUnitVariantFoundTupleVariant;
return Err(vec![Some(TypeError(e, meta))]);
}
_ => unreachable!(),
}
} else {
let e = TypeErrorEnum::UnknownEnumVariant(
enum_name.clone(),
variant_name.to_string(),
);
return Err(vec![Some(TypeError(e, meta))]);
}
} else {
let e = TypeErrorEnum::UnknownEnum(enum_name.clone());
return Err(vec![Some(TypeError(e, meta))]);
}
}
};
if let Some(ty) = ty {
Ok(Pattern::typed(pattern, ty, meta))
} else {
Err(vec![None])
}
}
}
fn check_exhaustiveness(
patterns: &[&TypedPattern],
ty: &Type,
defs: &Defs,
meta: MetaInfo,
) -> Result<(), TypeError> {
let patterns: Vec<Vec<TypedPattern>> = patterns.iter().map(|&p| vec![p.clone()]).collect();
let wildcard_pattern = vec![Pattern::typed(
PatternEnum::Identifier("_".to_string()),
ty.clone(),
meta,
)];
let witnesses = usefulness(patterns, wildcard_pattern, defs);
if !witnesses.is_empty() {
let e = TypeErrorEnum::PatternsAreNotExhaustive(witnesses);
Err(TypeError(e, meta))
} else {
Ok(())
}
}
#[derive(Debug, Clone)]
enum Ctor {
True,
False,
UnsignedInclusiveRange(UnsignedNumType, u64, u64),
SignedInclusiveRange(SignedNumType, i64, i64),
Tuple(Vec<Type>),
Struct(String, Vec<(String, Type)>),
Variant(String, String, Option<Vec<Type>>),
Array(Box<Type>, usize),
}
type PatternStack = Vec<TypedPattern>;
fn specialize(ctor: &Ctor, pattern: &[TypedPattern]) -> Vec<PatternStack> {
let head = if let Some(head) = pattern.first() {
head
} else {
return vec![];
};
let tail = pattern.iter().skip(1).cloned();
let Pattern(head_enum, meta, _) = head;
match ctor {
Ctor::True => match head_enum {
PatternEnum::Identifier(_) | PatternEnum::True => {
vec![tail.collect()]
}
_ => vec![],
},
Ctor::False => match head_enum {
PatternEnum::Identifier(_) | PatternEnum::False => {
vec![tail.collect()]
}
_ => vec![],
},
Ctor::UnsignedInclusiveRange(_, min, max) => match head_enum {
PatternEnum::Identifier(_) => vec![tail.collect()],
PatternEnum::NumUnsigned(n, _) if n == min && n == max => vec![tail.collect()],
PatternEnum::UnsignedInclusiveRange(n_min, n_max, _)
if n_min <= min && max <= n_max =>
{
vec![tail.collect()]
}
_ => vec![],
},
Ctor::SignedInclusiveRange(_, min, max) => match head_enum {
PatternEnum::Identifier(_) => vec![tail.collect()],
PatternEnum::NumUnsigned(n, _)
if *min >= 0 && *max >= 0 && *n == *min as u64 && *n == *max as u64 =>
{
vec![tail.collect()]
}
PatternEnum::NumSigned(n, _) if n == min && n == max => vec![tail.collect()],
PatternEnum::UnsignedInclusiveRange(n_min, n_max, _)
if *min >= 0 && *max >= 0 && *n_min <= *min as u64 && *max as u64 <= *n_max =>
{
vec![tail.collect()]
}
PatternEnum::SignedInclusiveRange(n_min, n_max, _) if n_min <= min && max <= n_max => {
vec![tail.collect()]
}
_ => vec![],
},
Ctor::Tuple(field_types) => match head_enum {
PatternEnum::Identifier(_) => {
let mut fields = Vec::with_capacity(field_types.len());
for ty in field_types {
let wildcard = PatternEnum::Identifier("_".to_string());
let p = Pattern::typed(wildcard, ty.clone(), *meta);
fields.push(p);
}
vec![fields.into_iter().chain(tail).collect()]
}
PatternEnum::Tuple(fields) => {
vec![fields.iter().cloned().chain(tail).collect()]
}
_ => vec![],
},
Ctor::Array(_, _) => match head_enum {
PatternEnum::Identifier(_) => vec![tail.collect()],
_ => vec![],
},
Ctor::Struct(struct_name, field_types) => match head_enum {
PatternEnum::Identifier(_) => {
let mut fields = Vec::with_capacity(field_types.len());
for (_, ty) in field_types {
let wildcard = PatternEnum::Identifier("_".to_string());
let p = Pattern::typed(wildcard, ty.clone(), *meta);
fields.push(p);
}
vec![fields.into_iter().chain(tail).collect()]
}
PatternEnum::Struct(struct_name_in_pattern, fields)
| PatternEnum::StructIgnoreRemaining(struct_name_in_pattern, fields)
if struct_name == struct_name_in_pattern =>
{
vec![fields
.iter()
.map(|(_, pattern)| pattern.clone())
.chain(tail)
.collect()]
}
_ => vec![],
},
Ctor::Variant(_, v1, fields) => match head_enum {
PatternEnum::Identifier(_) => {
let field_types = fields.as_deref().unwrap_or_default();
let mut fields = Vec::with_capacity(field_types.len());
for ty in field_types {
let wildcard = PatternEnum::Identifier("_".to_string());
let p = Pattern::typed(wildcard, ty.clone(), *meta);
fields.push(p);
}
vec![fields.into_iter().chain(tail).collect()]
}
PatternEnum::EnumUnit(_, v2) if v1 == v2 => vec![tail.collect()],
PatternEnum::EnumTuple(_, v2, fields) if v1 == v2 => {
vec![fields.iter().cloned().chain(tail).collect()]
}
_ => vec![],
},
}
}
fn split_unsigned_range(
ty: UnsignedNumType,
patterns: &[PatternStack],
min: u64,
max: u64,
) -> Vec<Ctor> {
let mut split_points = vec![min as u128, max as u128 + 1];
for p in patterns {
let head = p.first().unwrap();
let Pattern(head_enum, _, _) = head;
match head_enum {
PatternEnum::NumUnsigned(n, _) => split_points.push(*n as u128),
PatternEnum::NumSigned(n, _) if *n >= 0 => split_points.push(*n as u128),
PatternEnum::UnsignedInclusiveRange(min, max, _) => {
split_points.push(*min as u128);
split_points.push(*max as u128 + 1);
}
PatternEnum::SignedInclusiveRange(min, max, _) => {
if *min >= 0 {
split_points.push(*min as u128);
}
if *max >= 0 {
split_points.push(*max as u128 + 1);
}
}
_ => {}
}
}
split_points.sort_unstable();
split_points.dedup();
let mut ranges = vec![];
for range in split_points.windows(2) {
if range[0] < range[1] - 1 {
ranges.push(Ctor::UnsignedInclusiveRange(
ty,
range[0] as u64,
range[0] as u64,
));
}
if range[0] >= min as u128 && range[1] - 1 <= max as u128 {
if range[0] < range[1] - 1 {
ranges.push(Ctor::UnsignedInclusiveRange(
ty,
range[0] as u64 + 1,
range[1] as u64 - 1,
));
} else {
ranges.push(Ctor::UnsignedInclusiveRange(
ty,
range[0] as u64,
range[1] as u64 - 1,
));
}
}
}
ranges
}
fn split_signed_range(
ty: SignedNumType,
patterns: &[PatternStack],
min: i64,
max: i64,
) -> Vec<Ctor> {
let mut split_points = vec![min as i128, max as i128 + 1];
for p in patterns {
let head = p.first().unwrap();
let Pattern(head_enum, _, _) = head;
match head_enum {
PatternEnum::NumUnsigned(n, _) => split_points.push(*n as i128),
PatternEnum::NumSigned(n, _) if *n >= 0 => split_points.push(*n as i128),
PatternEnum::UnsignedInclusiveRange(min, max, _) => {
split_points.push(*min as i128);
split_points.push(*max as i128 + 1);
}
PatternEnum::SignedInclusiveRange(min, max, _) => {
if *min >= 0 {
split_points.push(*min as i128);
}
if *max >= 0 {
split_points.push(*max as i128 + 1);
}
}
_ => {}
}
}
split_points.sort_unstable();
split_points.dedup();
let mut ranges = vec![];
for range in split_points.windows(2) {
if range[0] < range[1] - 1 {
ranges.push(Ctor::SignedInclusiveRange(
ty,
range[0] as i64,
range[0] as i64,
));
}
if range[0] >= min as i128 && range[1] - 1 <= max as i128 {
ranges.push(Ctor::SignedInclusiveRange(
ty,
range[0] as i64,
range[1] as i64 - 1,
));
}
}
ranges
}
fn split_ctor(patterns: &[PatternStack], q: &[TypedPattern], defs: &Defs) -> Vec<Ctor> {
let head = q.first().unwrap();
let Pattern(head_enum, _, ty) = head;
match ty {
Type::Bool => vec![Ctor::True, Ctor::False],
Type::Unsigned(ty) => match head_enum {
PatternEnum::Identifier(_) => split_unsigned_range(*ty, patterns, 0, ty.max()),
PatternEnum::NumUnsigned(n, _) => {
vec![Ctor::UnsignedInclusiveRange(*ty, *n, *n)]
}
PatternEnum::UnsignedInclusiveRange(min, max, _) => {
split_unsigned_range(*ty, patterns, *min, *max)
}
_ => panic!("cannot split {:?} for type {:?}", head_enum, ty),
},
Type::Signed(ty) => match head_enum {
PatternEnum::Identifier(_) => {
vec![Ctor::SignedInclusiveRange(*ty, ty.min(), ty.max())]
}
PatternEnum::NumUnsigned(n, _) => {
vec![Ctor::SignedInclusiveRange(*ty, *n as i64, *n as i64)]
}
PatternEnum::NumSigned(n, _) => vec![Ctor::SignedInclusiveRange(*ty, *n, *n)],
PatternEnum::UnsignedInclusiveRange(min, max, _) => {
split_signed_range(*ty, patterns, *min as i64, *max as i64)
}
PatternEnum::SignedInclusiveRange(min, max, _) => {
split_signed_range(*ty, patterns, *min, *max)
}
_ => panic!("cannot split {:?} for type {:?}", head_enum, ty),
},
Type::Struct(struct_name) => {
let (fields, field_types) = defs.structs.get(struct_name.as_str()).unwrap();
let mut typed_fields = Vec::with_capacity(fields.len());
for field in fields {
typed_fields.push((field.to_string(), field_types.get(field).unwrap().clone()));
}
vec![Ctor::Struct(struct_name.clone(), typed_fields)]
}
Type::Enum(enum_name) => {
let variants = defs.enums.get(enum_name.as_str()).unwrap();
variants
.iter()
.map(|(name, fields)| {
Ctor::Variant(enum_name.clone(), name.to_string(), fields.clone())
})
.collect()
}
Type::Tuple(fields) => {
vec![Ctor::Tuple(fields.clone())]
}
Type::Array(elem_ty, size) => vec![Ctor::Array(elem_ty.clone(), *size)],
Type::Fn(_, _) => {
panic!("Type {:?} does not support pattern matching", ty)
}
Type::UntypedTopLevelDefinition(_, _) => {
unreachable!("Untyped top level types should have been typechecked at this point")
}
}
}
fn usefulness(patterns: Vec<PatternStack>, q: PatternStack, defs: &Defs) -> Vec<PatternStack> {
if patterns.is_empty() {
vec![q]
} else if patterns[0].is_empty() || q.is_empty() {
vec![]
} else {
let mut witnesses = vec![];
let meta = MetaInfo {
start: (0, 0),
end: (0, 0),
};
for ctor in split_ctor(&patterns, &q, defs) {
let mut specialized = Vec::new();
for p in patterns.iter() {
let pattern_specialized = specialize(&ctor, p);
specialized.extend(pattern_specialized);
}
for q in specialize(&ctor, &q) {
for mut witness in usefulness(specialized.clone(), q.clone(), defs) {
match &ctor {
Ctor::True => {
witness.insert(0, Pattern::typed(PatternEnum::True, Type::Bool, meta))
}
Ctor::False => {
witness.insert(0, Pattern::typed(PatternEnum::False, Type::Bool, meta))
}
Ctor::UnsignedInclusiveRange(ty, min, max) => witness.insert(
0,
Pattern::typed(
PatternEnum::UnsignedInclusiveRange(*min, *max, *ty),
Type::Unsigned(*ty),
meta,
),
),
Ctor::SignedInclusiveRange(ty, min, max) => witness.insert(
0,
Pattern::typed(
PatternEnum::SignedInclusiveRange(*min, *max, *ty),
Type::Signed(*ty),
meta,
),
),
Ctor::Tuple(fields) => {
witness = vec![Pattern::typed(
PatternEnum::Tuple(witness),
Type::Tuple(fields.clone()),
meta,
)]
}
Ctor::Struct(struct_name, fields) => {
let witness_fields: Vec<_> = fields
.iter()
.zip(witness.into_iter())
.map(|((field_name, _), pattern)| (field_name.clone(), pattern))
.collect();
witness = vec![Pattern::typed(
PatternEnum::Struct(struct_name.clone(), witness_fields),
Type::Struct(struct_name.clone()),
meta,
)]
}
Ctor::Variant(enum_name, variant_name, None) => {
witness = vec![Pattern::typed(
PatternEnum::EnumUnit(enum_name.clone(), variant_name.clone()),
Type::Enum(enum_name.clone()),
meta,
)]
}
Ctor::Variant(enum_name, variant_name, Some(_)) => {
witness = vec![Pattern::typed(
PatternEnum::EnumTuple(
enum_name.clone(),
variant_name.clone(),
witness,
),
Type::Enum(enum_name.clone()),
meta,
)]
}
Ctor::Array(elem_ty, size) => witness.insert(
0,
Pattern::typed(
PatternEnum::Identifier("_".to_string()),
Type::Array(elem_ty.clone(), *size),
meta,
),
),
}
witnesses.push(witness);
}
}
}
witnesses
}
}
fn expect_array_type(ty: &Type, meta: MetaInfo) -> Result<(Type, usize), TypeErrors> {
match ty {
Type::Array(elem, size) => Ok((*elem.clone(), *size)),
_ => Err(vec![Some(TypeError(
TypeErrorEnum::ExpectedArrayType(ty.clone()),
meta,
))]),
}
}
fn expect_struct_type(ty: &Type, meta: MetaInfo) -> Result<String, TypeErrors> {
match ty {
Type::Struct(name) => Ok(name.clone()),
_ => Err(vec![Some(TypeError(
TypeErrorEnum::ExpectedStructType(ty.clone()),
meta,
))]),
}
}
fn expect_tuple_type(ty: &Type, meta: MetaInfo) -> Result<Vec<Type>, TypeErrors> {
match ty {
Type::Tuple(types) => Ok(types.clone()),
_ => Err(vec![Some(TypeError(
TypeErrorEnum::ExpectedTupleType(ty.clone()),
meta,
))]),
}
}
fn expect_num_type(ty: &Type, meta: MetaInfo) -> Result<(), TypeErrors> {
match ty {
Type::Unsigned(_) | Type::Signed(_) => Ok(()),
_ => Err(vec![Some(TypeError(
TypeErrorEnum::ExpectedNumberType(ty.clone()),
meta,
))]),
}
}
fn expect_signed_num_type(ty: &Type, meta: MetaInfo) -> Result<(), TypeErrors> {
match ty {
Type::Signed(_) => Ok(()),
_ => Err(vec![Some(TypeError(
TypeErrorEnum::ExpectedSignedNumberType(ty.clone()),
meta,
))]),
}
}
fn expect_bool_or_num_type(ty: &Type, meta: MetaInfo) -> Result<(), TypeErrors> {
if let Type::Bool = ty {
return Ok(());
};
if let Ok(()) = expect_num_type(ty, meta) {
return Ok(());
}
Err(vec![Some(TypeError(
TypeErrorEnum::ExpectedBoolOrNumberType(ty.clone()),
meta,
))])
}
pub(crate) fn check_type(expr: &mut TypedExpr, expected: &Type) -> Result<(), TypeErrors> {
let Expr(_, meta, actual) = &expr;
if actual == expected {
Ok(())
} else {
let expected = expected.clone();
let e = TypeErrorEnum::UnexpectedType {
expected,
actual: actual.clone(),
};
Err(vec![Some(TypeError(e, *meta))])
}
}
fn unify(e1: &mut TypedExpr, e2: &mut TypedExpr, m: MetaInfo) -> Result<Type, TypeErrors> {
let Expr(expr1, meta1, ty1) = e1;
let Expr(expr2, meta2, ty2) = e2;
let ty = match (expr1, expr2) {
_ if *ty1 == *ty2 => Ok(ty1.clone()),
_ => {
let e = TypeErrorEnum::TypeMismatch((ty1.clone(), *meta1), (ty2.clone(), *meta2));
Err(vec![Some(TypeError(e, m))])
}
};
if let Ok(ty) = &ty {
e1.2 = ty.clone();
e2.2 = ty.clone();
}
ty
}