Skip to content

Commit

Permalink
Merge pull request #160 from gio54321/type-checker-errors
Browse files Browse the repository at this point in the history
Improve type checker errors
  • Loading branch information
katat authored Aug 22, 2024
2 parents 7779b71 + 533c12c commit e68cab0
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 15 deletions.
21 changes: 21 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -281,12 +281,18 @@ pub enum ErrorKind {
#[error("method call can only be applied on custom structs")]
MethodCallOnNonCustomStruct,

#[error("field access can only be applied on custom structs")]
FieldAccessOnNonCustomStruct,

#[error("array access can only be performed on arrays")]
ArrayAccessOnNonArray,

#[error("struct `{0}` does not exist (are you sure it is defined?)")]
UndefinedStruct(String),

#[error("struct `{0}` does not have a method called `{1}`")]
UndefinedMethod(String, String),

#[error("struct `{0}` does not have a field called `{1}`")]
UndefinedField(String, String),

Expand All @@ -301,4 +307,19 @@ pub enum ErrorKind {

#[error("invalid hexadecimal literal `${0}`")]
InvalidHexLiteral(String),

#[error("if-else condition is expected to be a boolean, but has type `{0}`")]
IfElseInvalidConditionType(TyKind),

#[error("`if` branch must be a variable, a field access, or an array access. It can't be logic that creates constraints.")]
IfElseInvalidIfBranch(),

#[error("`else` branch must be a variable, a field access, or an array access. It can't be logic that creates constraints.")]
IfElseInvalidElseBranch(),

#[error("`if` branch and `else` branch must have matching types")]
IfElseMismatchingBranchesTypes(),

#[error("invalid range, the end value can't be smaller than the start value")]
InvalidRange,
}
46 changes: 31 additions & 15 deletions src/type_checker/checker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ impl<B: Backend> TypeChecker<B> {
// obtain the type of the field
let (module, struct_name) = match lhs_node.typ {
TyKind::Custom { module, name } => (module, name),
_ => panic!("field access must be done on a custom struct"),
_ => return Err(self.error(ErrorKind::FieldAccessOnNonCustomStruct, expr.span)),
};

// get struct info
Expand All @@ -104,10 +104,16 @@ impl<B: Backend> TypeChecker<B> {
.fields
.iter()
.find(|(name, _)| name == &rhs.value)
.map(|(_, typ)| typ.clone())
.expect("could not find field");
.map(|(_, typ)| typ.clone());

Some(ExprTyInfo::new(lhs_node.var_name, res))
if let Some(res) = res {
Some(ExprTyInfo::new(lhs_node.var_name, res))
} else {
return Err(self.error(
ErrorKind::UndefinedField(struct_info.name.clone(), rhs.value.clone()),
expr.span,
));
}
}

// `module::fn_name(args)`
Expand Down Expand Up @@ -154,10 +160,15 @@ impl<B: Backend> TypeChecker<B> {
.clone();

// get method info
let method_type = struct_info
.methods
.get(&method_name.value)
.expect("method not found on custom struct (TODO: better error)");
let method_type = struct_info.methods.get(&method_name.value);

if method_type.is_none() {
return Err(self.error(
ErrorKind::UndefinedMethod(struct_name.clone(), method_name.value.clone()),
method_name.span,
));
}
let method_type = method_type.unwrap();

// type check the method call
let method_call = true;
Expand Down Expand Up @@ -238,7 +249,10 @@ impl<B: Backend> TypeChecker<B> {
let rhs_typ = self.compute_type(rhs, typed_fn_env)?.unwrap();

if !rhs_typ.typ.match_expected(&lhs_node.typ) {
panic!("lhs type doesn't match rhs type (TODO: replace with error)");
return Err(self.error(
ErrorKind::MismatchType(lhs_node.typ.clone(), rhs_typ.typ.clone()),
expr.span,
));
}

None
Expand Down Expand Up @@ -412,7 +426,10 @@ impl<B: Backend> TypeChecker<B> {
.compute_type(cond, typed_fn_env)?
.expect("can't compute type of condition");
if !matches!(cond_node.typ, TyKind::Bool) {
panic!("`if` must be followed by a boolean");
return Err(self.error(
ErrorKind::IfElseInvalidConditionType(cond_node.typ.clone()),
cond.span,
));
}

// then_ and else_ can only be variables, field accesses, or array accesses
Expand All @@ -422,7 +439,7 @@ impl<B: Backend> TypeChecker<B> {
| ExprKind::FieldAccess { .. }
| ExprKind::ArrayAccess { .. }
) {
panic!("`if` branch must be a variable, a field access, or an array access. It can't be logic that creates constraints.");
return Err(self.error(ErrorKind::IfElseInvalidIfBranch(), then_.span));
}

if !matches!(
Expand All @@ -431,7 +448,7 @@ impl<B: Backend> TypeChecker<B> {
| ExprKind::FieldAccess { .. }
| ExprKind::ArrayAccess { .. }
) {
panic!("`else` branch must be a variable, a field access, or an array access. It can't be logic that creates constraints.");
return Err(self.error(ErrorKind::IfElseInvalidElseBranch(), else_.span));
}

// compute type of if/else branches
Expand All @@ -444,10 +461,9 @@ impl<B: Backend> TypeChecker<B> {

// make sure that the type of then_ and else_ match
if then_node.typ != else_node.typ {
panic!("`if` branch and `else` branch must have matching types");
return Err(self.error(ErrorKind::IfElseMismatchingBranchesTypes(), expr.span));
}

//
Some(ExprTyInfo::new_anon(then_node.typ))
}

Expand Down Expand Up @@ -585,7 +601,7 @@ impl<B: Backend> TypeChecker<B> {

// ensure start..end makes sense
if range.end < range.start {
panic!("end can't be smaller than start (TODO: better error)");
return Err(self.error(ErrorKind::InvalidRange, range.span));
}

// check block
Expand Down

0 comments on commit e68cab0

Please sign in to comment.