From fc2927c9713f8640f4685bae4659bd0af52020ea Mon Sep 17 00:00:00 2001 From: Volodymyr Herashchenko Date: Mon, 25 May 2026 11:29:57 +0300 Subject: [PATCH 1/4] change path related errors to use PathBuf --- src/error.rs | 18 ++++++++++++++---- src/resolution.rs | 47 +++++++++++++++++++++++++---------------------- 2 files changed, 39 insertions(+), 26 deletions(-) diff --git a/src/error.rs b/src/error.rs index cc49121d..e4387ea6 100644 --- a/src/error.rs +++ b/src/error.rs @@ -473,8 +473,12 @@ impl fmt::Display for ErrorCollector { /// Records _what_ happened but not where. #[derive(Debug, Clone)] pub enum Error { - DependencyPathNotFound(String), - DependencyNotADirectory(String), + DependencyPathNotFound { + path: PathBuf, + }, + DependencyNotADirectory { + path: PathBuf, + }, ReservedDependencyKeyword(String), DuplicateDependencyAlias(String, String), InvalidDependencyIdentifier(String), @@ -546,8 +550,14 @@ pub enum Error { impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - Error::DependencyPathNotFound(path) => write!(f, "Path not found: {}", path), - Error::DependencyNotADirectory(path) => write!(f, "Path must be a directory: {}", path), + Error::DependencyPathNotFound { path } => write!( + f, + "Path not found: {}", path.display() + ), + Error::DependencyNotADirectory { path } => write!( + f, + "Path must be a directory: {}", path.display() + ), Error::ReservedDependencyKeyword(kw) => write!(f, "The '{}' keyword is reserved and cannot be manually mapped. Use the builder's context definitions instead.", kw), Error::DuplicateDependencyAlias(alias, context) => write!(f, "Duplicate dependency mapping: alias '{}' is defined multiple times for context '{}'", alias, context), Error::InvalidDependencyIdentifier(alias) => write!(f, "Invalid dependency alias '{}': must be a valid identifier and not a reserved keyword", alias), diff --git a/src/resolution.rs b/src/resolution.rs index a064c906..56a4efcf 100644 --- a/src/resolution.rs +++ b/src/resolution.rs @@ -84,37 +84,37 @@ impl DependencyMapBuilder { // will always return `Some()` for files under the entry root. let root = self.entry_root; if !root.as_path().exists() { - return Err(Error::DependencyPathNotFound( - root.as_path().display().to_string(), - )); + return Err(Error::DependencyPathNotFound { + path: root.as_path().into(), + }); } if !root.as_path().is_dir() { - return Err(Error::DependencyNotADirectory( - root.as_path().display().to_string(), - )); + return Err(Error::DependencyNotADirectory { + path: root.as_path().into(), + }); } crate_roots.push(root); for dep in self.deps { if !dep.context_prefix.as_path().exists() { - return Err(Error::DependencyPathNotFound( - dep.context_prefix.as_path().display().to_string(), - )); + return Err(Error::DependencyPathNotFound { + path: dep.context_prefix.as_path().into(), + }); } if !dep.context_prefix.as_path().is_dir() { - return Err(Error::DependencyNotADirectory( - dep.context_prefix.as_path().display().to_string(), - )); + return Err(Error::DependencyNotADirectory { + path: dep.context_prefix.as_path().into(), + }); } if !dep.target.as_path().exists() { - return Err(Error::DependencyPathNotFound( - dep.target.as_path().display().to_string(), - )); + return Err(Error::DependencyPathNotFound { + path: dep.target.as_path().into(), + }); } if !dep.target.as_path().is_dir() { - return Err(Error::DependencyNotADirectory( - dep.target.as_path().display().to_string(), - )); + return Err(Error::DependencyNotADirectory { + path: dep.target.as_path().into(), + }); } if !is_valid_dependency_identifier(&dep.drp_name) { @@ -532,7 +532,7 @@ pub(crate) mod tests { let res1 = DependencyMapBuilder::new(file_path.clone()).build(); assert!(matches!( res1.unwrap_err(), - Error::DependencyNotADirectory(_) + Error::DependencyNotADirectory { .. } )); let res2 = DependencyMapBuilder::new(valid_dir.clone()) @@ -540,7 +540,7 @@ pub(crate) mod tests { .build(); assert!(matches!( res2.unwrap_err(), - Error::DependencyNotADirectory(_) + Error::DependencyNotADirectory { .. } )); let res3 = DependencyMapBuilder::new(valid_dir.clone()) @@ -548,7 +548,7 @@ pub(crate) mod tests { .build(); assert!(matches!( res3.unwrap_err(), - Error::DependencyNotADirectory(_) + Error::DependencyNotADirectory { .. } )); } @@ -561,7 +561,10 @@ pub(crate) mod tests { let res = DependencyMapBuilder::new(valid_dir.clone()) .add_dependency(valid_dir.clone(), "alias".to_string(), fake_path) .build(); - assert!(matches!(res.unwrap_err(), Error::DependencyPathNotFound(_))); + assert!(matches!( + res.unwrap_err(), + Error::DependencyPathNotFound { .. } + )); } #[test] From 84c529d75096d0d4ddd758c02e76285972bd8242 Mon Sep 17 00:00:00 2001 From: Volodymyr Herashchenko Date: Mon, 25 May 2026 11:42:52 +0300 Subject: [PATCH 2/4] replace new errors of resolution to named enum --- src/error.rs | 28 ++++++++++++++++++++++------ src/resolution.rs | 26 +++++++++++++++----------- 2 files changed, 37 insertions(+), 17 deletions(-) diff --git a/src/error.rs b/src/error.rs index e4387ea6..7a6f1e58 100644 --- a/src/error.rs +++ b/src/error.rs @@ -479,9 +479,16 @@ pub enum Error { DependencyNotADirectory { path: PathBuf, }, - ReservedDependencyKeyword(String), - DuplicateDependencyAlias(String, String), - InvalidDependencyIdentifier(String), + ReservedDependencyKeyword { + keyword: String, + }, + DuplicateDependencyAlias { + alias: String, + context: String, + }, + InvalidDependencyIdentifier { + alias: String, + }, Internal(String), UnknownLibrary(String), ArraySizeNonZero(usize), @@ -558,9 +565,18 @@ impl fmt::Display for Error { f, "Path must be a directory: {}", path.display() ), - Error::ReservedDependencyKeyword(kw) => write!(f, "The '{}' keyword is reserved and cannot be manually mapped. Use the builder's context definitions instead.", kw), - Error::DuplicateDependencyAlias(alias, context) => write!(f, "Duplicate dependency mapping: alias '{}' is defined multiple times for context '{}'", alias, context), - Error::InvalidDependencyIdentifier(alias) => write!(f, "Invalid dependency alias '{}': must be a valid identifier and not a reserved keyword", alias), + Error::ReservedDependencyKeyword { keyword } => write!( + f, + "The '{keyword}' keyword is reserved and cannot be manually mapped. Use the builder's context definitions instead." + ), + Error::DuplicateDependencyAlias { alias, context } => write!( + f, + "Duplicate dependency mapping: alias '{alias}' is defined multiple times for context '{context}'" + ), + Error::InvalidDependencyIdentifier { alias } => write!( + f, + "Invalid dependency alias '{alias}': must be a valid identifier and not a reserved keyword" + ), Error::Internal(err) => write!( f, "INTERNAL ERROR: {err}" diff --git a/src/resolution.rs b/src/resolution.rs index 56a4efcf..44da2bd5 100644 --- a/src/resolution.rs +++ b/src/resolution.rs @@ -119,19 +119,23 @@ impl DependencyMapBuilder { if !is_valid_dependency_identifier(&dep.drp_name) { if dep.drp_name == CRATE_STR { - return Err(Error::ReservedDependencyKeyword(dep.drp_name)); + return Err(Error::ReservedDependencyKeyword { + keyword: dep.drp_name, + }); } - return Err(Error::InvalidDependencyIdentifier(dep.drp_name)); + return Err(Error::InvalidDependencyIdentifier { + alias: dep.drp_name, + }); } // Reject duplicates: same context and same alias if remappings.iter().any(|r: &Remapping| { r.context_prefix == dep.context_prefix && r.drp_name == dep.drp_name }) { - return Err(Error::DuplicateDependencyAlias( - dep.drp_name.clone(), - dep.context_prefix.as_path().display().to_string(), - )); + return Err(Error::DuplicateDependencyAlias { + alias: dep.drp_name.clone(), + context: dep.context_prefix.as_path().display().to_string(), + }); } crate_roots.push(dep.target.clone()); @@ -345,7 +349,7 @@ pub(crate) mod tests { assert!(matches!( result.unwrap_err(), - Error::ReservedDependencyKeyword(_) + Error::ReservedDependencyKeyword { .. } )); } @@ -579,7 +583,7 @@ pub(crate) mod tests { .add_dependency(valid_dir.clone(), bad_alias.to_string(), valid_dir.clone()) .build(); assert!( - matches!(res.unwrap_err(), Error::InvalidDependencyIdentifier(_)), + matches!(res.unwrap_err(), Error::InvalidDependencyIdentifier { .. }), "Builder should reject alias: '{}'", bad_alias ); @@ -599,9 +603,9 @@ pub(crate) mod tests { .build(); let err = res.unwrap_err(); if kw == CRATE_STR { - assert!(matches!(err, Error::ReservedDependencyKeyword(_))); + assert!(matches!(err, Error::ReservedDependencyKeyword { .. })); } else { - assert!(matches!(err, Error::InvalidDependencyIdentifier(_))); + assert!(matches!(err, Error::InvalidDependencyIdentifier { .. })); } } } @@ -620,7 +624,7 @@ pub(crate) mod tests { assert!(matches!( res.unwrap_err(), - Error::DuplicateDependencyAlias(..) + Error::DuplicateDependencyAlias { .. } )); } From 15b9dfcffb98dcb12fc58e01a79c8ec9bbe6f96c Mon Sep 17 00:00:00 2001 From: Volodymyr Herashchenko Date: Mon, 25 May 2026 13:00:16 +0300 Subject: [PATCH 3/4] change most of the errors to the named enum --- src/ast.rs | 70 ++++++----- src/driver/mod.rs | 9 +- src/driver/resolve_order.rs | 38 ++++-- src/error.rs | 233 +++++++++++++++++++++++------------- src/lexer.rs | 4 +- src/parse.rs | 55 ++++++--- src/resolution.rs | 44 ++++--- src/value.rs | 10 +- src/witness.rs | 20 ++-- 9 files changed, 314 insertions(+), 169 deletions(-) diff --git a/src/ast.rs b/src/ast.rs index 756d49d5..54420109 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -735,11 +735,11 @@ impl Scope { .aliases_table .local_scopes() .get(self.file_id) - .ok_or_else(|| { - Error::Internal(format!( + .ok_or_else(|| Error::Internal { + msg: format!( "file_id {} not found inside current Scope aliases", self.file_id - )) + ), })?; // 4. Verify local scope visibility. @@ -747,7 +747,9 @@ impl Scope { // We clone here because types usually need to be owned in AST resolution Ok(resolved_type.clone()) } else { - Err(Error::PrivateItem(name.as_inner().to_string())) + Err(Error::PrivateItem { + name: name.as_inner().to_string(), + }) } } @@ -785,7 +787,10 @@ impl Scope { pub fn insert_parameter(&mut self, name: WitnessName, ty: ResolvedType) -> Result<(), Error> { match self.parameters.entry(name.clone()) { Entry::Occupied(entry) if entry.get() == &ty => Ok(()), - Entry::Occupied(entry) => Err(Error::ExpressionTypeMismatch(entry.get().clone(), ty)), + Entry::Occupied(entry) => Err(Error::ExpressionTypeMismatch { + expected: entry.get().clone(), + found: ty, + }), Entry::Vacant(entry) => { entry.insert(ty); Ok(()) @@ -882,11 +887,11 @@ impl Scope { .functions_table .local_scopes() .get(self.file_id) - .ok_or_else(|| { - Error::Internal(format!( + .ok_or_else(|| Error::Internal { + msg: format!( "file_id {} not found inside current Scope files", self.file_id - )) + ), })?; // 3. Verify local scope visibility. @@ -894,7 +899,9 @@ impl Scope { if file_scope.contains(name) { Ok(function) } else { - Err(Error::PrivateItem(name.as_inner().to_string())) + Err(Error::PrivateItem { + name: name.as_inner().to_string(), + }) } } @@ -1123,10 +1130,10 @@ impl AbstractSyntaxTree for Expression { .map(Arc::new) .map(Some), None if ty.is_unit() => Ok(None), - None => Err(Error::ExpressionTypeMismatch( - ty.clone(), - ResolvedType::unit(), - )) + None => Err(Error::ExpressionTypeMismatch { + expected: ty.clone(), + found: ResolvedType::unit(), + }) .with_span(from), }?; scope.pop_scope(); @@ -1148,10 +1155,10 @@ impl AbstractSyntaxTree for SingleExpression { let inner = match from.inner() { parse::SingleExpressionInner::Boolean(bit) => { if !ty.is_boolean() { - return Err(Error::ExpressionTypeMismatch( - ty.clone(), - ResolvedType::boolean(), - )) + return Err(Error::ExpressionTypeMismatch { + expected: ty.clone(), + found: ResolvedType::boolean(), + }) .with_span(from); } SingleExpressionInner::Constant(Value::from(*bit)) @@ -1196,8 +1203,11 @@ impl AbstractSyntaxTree for SingleExpression { .ok_or(Error::UndefinedVariable(identifier.clone())) .with_span(from)?; if ty != bound_ty { - return Err(Error::ExpressionTypeMismatch(ty.clone(), bound_ty.clone())) - .with_span(from); + return Err(Error::ExpressionTypeMismatch { + expected: ty.clone(), + found: bound_ty.clone(), + }) + .with_span(from); } scope.insert_variable(identifier.clone(), ty.clone()); SingleExpressionInner::Variable(identifier.clone()) @@ -1304,10 +1314,10 @@ impl AbstractSyntaxTree for Call { if parse_args.len() == expected_tys.len() { Ok(()) } else { - Err(Error::InvalidNumberOfArguments( - expected_tys.len(), - parse_args.len(), - )) + Err(Error::InvalidNumberOfArguments { + expected: expected_tys.len(), + found: parse_args.len(), + }) } } @@ -1318,10 +1328,10 @@ impl AbstractSyntaxTree for Call { if observed_ty == expected_ty { Ok(()) } else { - Err(Error::ExpressionTypeMismatch( - expected_ty.clone(), - observed_ty.clone(), - )) + Err(Error::ExpressionTypeMismatch { + expected: expected_ty.clone(), + found: observed_ty.clone(), + }) } } @@ -1412,7 +1422,11 @@ impl AbstractSyntaxTree for Call { } CallName::TypeCast(source) => { if StructuralType::from(&source) != StructuralType::from(ty) { - return Err(Error::InvalidCast(source, ty.clone())).with_span(from); + return Err(Error::InvalidCast { + source, + target: ty.clone(), + }) + .with_span(from); } let args_tys = [source]; diff --git a/src/driver/mod.rs b/src/driver/mod.rs index 08110d3d..0f026f84 100644 --- a/src/driver/mod.rs +++ b/src/driver/mod.rs @@ -206,8 +206,13 @@ impl DependencyGraph { handler: &mut ErrorCollector, ) -> Option { let Ok(content) = std::fs::read_to_string(path.as_path()) else { - let err = RichError::new(Error::FileNotFound(PathBuf::from(path.as_path())), span) - .with_source(importer_source.clone()); + let err = RichError::new( + Error::FileNotFound { + filename: PathBuf::from(path.as_path()), + }, + span, + ) + .with_source(importer_source.clone()); handler.push(err); return None; diff --git a/src/driver/resolve_order.rs b/src/driver/resolve_order.rs index d4e90d96..3a5524b7 100644 --- a/src/driver/resolve_order.rs +++ b/src/driver/resolve_order.rs @@ -41,8 +41,13 @@ impl Program { for item in parsed.items() { if let parse::Item::Use(use_decl) = item { handler.push( - RichError::new(Error::UnknownLibrary(use_decl.str_path()), *use_decl.span()) - .with_content(content.clone()), + RichError::new( + Error::UnknownLibrary { + name: use_decl.str_path(), + }, + *use_decl.span(), + ) + .with_content(content.clone()), ); continue; } @@ -259,8 +264,8 @@ impl DependencyGraph { (Ok(()), _) | (_, Ok(())) => Ok(()), (Err(err_alias), Err(err_func)) => { - let alias_is_missing = matches!(err_alias.error(), Error::UnresolvedItem(_)); - let func_is_missing = matches!(err_func.error(), Error::UnresolvedItem(_)); + let alias_is_missing = matches!(err_alias.error(), Error::UnresolvedItem { .. }); + let func_is_missing = matches!(err_func.error(), Error::UnresolvedItem { .. }); if !alias_is_missing || func_is_missing { // If it's missing everywhere, OR if the function is missing @@ -315,13 +320,26 @@ impl DependencyGraph { let orig_id = (target_name.clone(), ind); // 2. Verify Existence using T - let visibility: &Visibility = namespace.resolutions[ind] - .get(&target_name) - .ok_or_else(|| RichError::new(Error::UnresolvedItem(name.to_string()), span))?; + let visibility: &Visibility = + namespace.resolutions[ind] + .get(&target_name) + .ok_or_else(|| { + RichError::new( + Error::UnresolvedItem { + name: name.to_string(), + }, + span, + ) + })?; // 3. Verify Visibility if matches!(visibility, parse::Visibility::Private) { - return Err(RichError::new(Error::PrivateItem(name.to_string()), span)); + return Err(RichError::new( + Error::PrivateItem { + name: name.to_string(), + }, + span, + )); } // 4. Determine the local name and ID up front @@ -352,7 +370,9 @@ impl DependencyGraph { if namespace.memo.contains(&local_id) { return Err(RichError::new( - Error::RedefinedItem(local_symbol.to_string()), + Error::RedefinedItem { + name: local_symbol.to_string(), + }, span, )); } diff --git a/src/error.rs b/src/error.rs index 7a6f1e58..42b16bc4 100644 --- a/src/error.rs +++ b/src/error.rs @@ -203,7 +203,9 @@ impl RichError { /// a problem on the parsing side. pub fn parsing_error(reason: &str) -> Self { Self { - error: Box::new(Error::CannotParse(reason.to_string())), + error: Box::new(Error::CannotParse { + msg: reason.to_string(), + }), span: Span::new(0, 0), source: None, } @@ -321,9 +323,9 @@ where { fn merge(self, other: Self) -> Self { match (self.error.as_ref(), other.error.as_ref()) { - (Error::Grammar(_), Error::Grammar(_)) => other, - (Error::Grammar(_), _) => other, - (_, Error::Grammar(_)) => self, + (Error::Grammar { .. }, Error::Grammar { .. }) => other, + (Error::Grammar { .. }, _) => other, + (_, Error::Grammar { .. }) => self, _ => other, } } @@ -489,19 +491,36 @@ pub enum Error { InvalidDependencyIdentifier { alias: String, }, - Internal(String), - UnknownLibrary(String), - ArraySizeNonZero(usize), - ListBoundPow2(usize), - BitStringPow2(usize), - CannotParse(String), - Grammar(String), + Internal { + msg: String, + }, + UnknownLibrary { + name: String, + }, + ArraySizeNonZero { + size: usize, + }, + ListBoundPow2 { + bound: usize, + }, + BitStringPow2 { + len: usize, + }, + CannotParse { + msg: String, + }, + Grammar { + msg: String, + }, Syntax { expected: Vec, label: Option, found: Option, }, - IncompatibleMatchArms(MatchPattern, MatchPattern), + IncompatibleMatchArms { + first: MatchPattern, + second: MatchPattern, + }, // TODO: Remove CompileError once SimplicityHL has a type system // The SimplicityHL compiler should never produce ill-typed Simplicity code // The compiler can only be this precise if it knows a type system at least as expressive as Simplicity's @@ -515,13 +534,29 @@ pub enum Error { source: crate::num::ParseIntError, }, JetDoesNotExist(JetName), - InvalidCast(ResolvedType, ResolvedType), - FileNotFound(PathBuf), - ExternalFileNotFound(String, PathBuf), - LocalFileImportedAsExternal(PathBuf), - RedefinedItem(String), - UnresolvedItem(String), - PrivateItem(String), + InvalidCast { + source: ResolvedType, + target: ResolvedType, + }, + FileNotFound { + filename: PathBuf, + }, + ExternalFileNotFound { + lib: String, + filename: PathBuf, + }, + LocalFileImportedAsExternal { + path: PathBuf, + }, + RedefinedItem { + name: String, + }, + UnresolvedItem { + name: String, + }, + PrivateItem { + name: String, + }, MainNoInputs, MainNoOutput, MainRequired, @@ -530,11 +565,17 @@ pub enum Error { MainCannotBeAlias, FunctionRedefined(FunctionName), FunctionUndefined(FunctionName), - InvalidNumberOfArguments(usize, usize), + InvalidNumberOfArguments { + expected: usize, + found: usize, + }, FunctionNotFoldable(FunctionName), FunctionNotLoopable(FunctionName), ExpressionUnexpectedType(ResolvedType), - ExpressionTypeMismatch(ResolvedType, ResolvedType), + ExpressionTypeMismatch { + expected: ResolvedType, + found: ResolvedType, + }, ExpressionNotConstant, IntegerOutOfBounds(UIntType), UndefinedVariable(Identifier), @@ -544,12 +585,20 @@ pub enum Error { DuplicateAlias(String), VariableReuseInPattern(Identifier), WitnessReused(WitnessName), - WitnessTypeMismatch(WitnessName, ResolvedType, ResolvedType), + WitnessTypeMismatch { + name: WitnessName, + declared: ResolvedType, + assigned: ResolvedType, + }, WitnessReassigned(WitnessName), WitnessOutsideMain, ModuleRedefined(ModuleName), ArgumentMissing(WitnessName), - ArgumentTypeMismatch(WitnessName, ResolvedType, ResolvedType), + ArgumentTypeMismatch { + name: WitnessName, + declared: ResolvedType, + assigned: ResolvedType, + }, UseKeywordIsNotSupported, } @@ -577,43 +626,43 @@ impl fmt::Display for Error { f, "Invalid dependency alias '{alias}': must be a valid identifier and not a reserved keyword" ), - Error::Internal(err) => write!( + Error::Internal { msg } => write!( f, - "INTERNAL ERROR: {err}" + "INTERNAL ERROR: {msg}" ), - Error::UnknownLibrary(name) => write!( + Error::UnknownLibrary { name } => write!( f, "Unknown module or library '{name}'" ), - Error::ArraySizeNonZero(size) => write!( + Error::ArraySizeNonZero { size } => write!( f, "Expected a non-negative integer as array size, found {size}" ), - Error::ListBoundPow2(bound) => write!( + Error::ListBoundPow2 { bound } => write!( f, "Expected a power of two greater than one (2, 4, 8, 16, 32, ...) as list bound, found {bound}" ), - Error::BitStringPow2(len) => write!( + Error::BitStringPow2 { len } => write!( f, "Expected a valid bit string length (1, 2, 4, 8, 16, 32, 64, 128, 256), found {len}" ), - Error::CannotParse(description) => write!( + Error::CannotParse{ msg } => write!( f, - "Cannot parse: {description}" + "Cannot parse: {msg}" ), - Error::Grammar(description) => write!( + Error::Grammar{ msg } => write!( f, - "Grammar error: {description}" + "Grammar error: {msg}" ), - Error::FileNotFound(path) => write!( + Error::FileNotFound { filename: path } => write!( f, "Local file `{}` not found", path.to_string_lossy() ), - Error::ExternalFileNotFound(lib, path) => write!( + Error::ExternalFileNotFound { lib, filename: path } => write!( f, "File `{}` not found in external library `{}`", path.to_string_lossy(), lib ), - Error::LocalFileImportedAsExternal(path) => write!( + Error::LocalFileImportedAsExternal { path } => write!( f, "File `{}` is part of the local project and must be imported using the `crate::` prefix", path.to_string_lossy() ), @@ -632,9 +681,9 @@ impl fmt::Display for Error { } } } - Error::IncompatibleMatchArms(pattern1, pattern2) => write!( + Error::IncompatibleMatchArms { first, second} => write!( f, - "Match arm `{pattern1}` is incompatible with arm `{pattern2}`" + "Match arm `{first}` is incompatible with arm `{second}`" ), Error::CannotCompile{ .. } => write!( f, @@ -645,7 +694,7 @@ impl fmt::Display for Error { f, "Jet `{name}` does not exist" ), - Error::InvalidCast(source, target) => write!( + Error::InvalidCast { source, target } => write!( f, "Cannot cast values of type `{source}` as values of type `{target}`" ), @@ -681,19 +730,19 @@ impl fmt::Display for Error { f, "Function `{name}` was called but not defined" ), - Error::RedefinedItem(name) => write!( + Error::RedefinedItem { name } => write!( f, "Item `{name}` was defined multiple times" ), - Error::UnresolvedItem(name) => write!( + Error::UnresolvedItem { name } => write!( f, "Item `{name}` could not be found" ), - Error::PrivateItem(name) => write!( + Error::PrivateItem { name } => write!( f, "Item `{name}` is private" ), - Error::InvalidNumberOfArguments(expected, found) => write!( + Error::InvalidNumberOfArguments { expected, found } => write!( f, "Expected {expected} arguments, found {found} arguments" ), @@ -709,7 +758,7 @@ impl fmt::Display for Error { f, "Expected expression of type `{ty}`; found something else" ), - Error::ExpressionTypeMismatch(expected, found) => write!( + Error::ExpressionTypeMismatch { expected, found } => write!( f, "Expected expression of type `{expected}`, found type `{found}`" ), @@ -749,7 +798,7 @@ impl fmt::Display for Error { f, "Witness `{name}` has been used before somewhere in the program" ), - Error::WitnessTypeMismatch(name, declared, assigned) => write!( + Error::WitnessTypeMismatch { name, declared, assigned } => write!( f, "Witness `{name}` was declared with type `{declared}` but its assigned value is of type `{assigned}`" ), @@ -769,7 +818,7 @@ impl fmt::Display for Error { f, "Parameter `{name}` is missing an argument" ), - Error::ArgumentTypeMismatch(name, declared, assigned) => write!( + Error::ArgumentTypeMismatch { name, declared, assigned } => write!( f, "Parameter `{name}` was declared with type `{declared}` but its assigned argument is of type `{assigned}`" ), @@ -829,7 +878,7 @@ let x: u32 = Left( #[test] fn display_single_line() { - let error = Error::ListBoundPow2(5) + let error = Error::ListBoundPow2 { bound: 5 } .with_span(Span::new(13, 19)) .with_content(Arc::from(CONTENT)); let expected = r#" @@ -841,9 +890,9 @@ let x: u32 = Left( #[test] fn display_multi_line() { - let error = Error::CannotParse( - "Expected value of type `u32`, got `Either, _>`".to_string(), - ) + let error = Error::CannotParse { + msg: "Expected value of type `u32`, got `Either, _>`".to_string(), + } .with_span(Span::new(41, CONTENT.len())) .with_content(Arc::from(CONTENT)); let expected = r#" @@ -857,9 +906,11 @@ let x: u32 = Left( #[test] fn display_entire_file() { - let error = Error::CannotParse("This span covers the entire file".to_string()) - .with_span(Span::from(CONTENT)) - .with_content(Arc::from(CONTENT)); + let error = Error::CannotParse { + msg: "This span covers the entire file".to_string(), + } + .with_span(Span::from(CONTENT)) + .with_content(Arc::from(CONTENT)); let expected = r#" | 1 | let a1: List = None; @@ -872,21 +923,27 @@ let x: u32 = Left( #[test] fn display_no_file() { - let error = Error::CannotParse("This error has no file".to_string()) - .with_span(Span::from(EMPTY_FILE)); + let error = Error::CannotParse { + msg: "This error has no file".to_string(), + } + .with_span(Span::from(EMPTY_FILE)); let expected = "Cannot parse: This error has no file"; assert_eq!(&expected, &error.to_string()); - let error = - Error::CannotParse("This error has no file".to_string()).with_span(Span::new(5, 10)); + let error = Error::CannotParse { + msg: "This error has no file".to_string(), + } + .with_span(Span::new(5, 10)); assert_eq!(&expected, &error.to_string()); } #[test] fn display_empty_file() { - let error = Error::CannotParse("This error has an empty file".to_string()) - .with_span(Span::from(EMPTY_FILE)) - .with_content(Arc::from(EMPTY_FILE)); + let error = Error::CannotParse { + msg: "This error has an empty file".to_string(), + } + .with_span(Span::from(EMPTY_FILE)) + .with_content(Arc::from(EMPTY_FILE)); let expected = "Cannot parse: This error has an empty file"; assert_eq!(&expected, &error.to_string()); } @@ -894,9 +951,11 @@ let x: u32 = Left( #[test] fn display_with_utf16_chars() { let file = "/*😀*/ let a: u8 = 65536;"; - let error = Error::CannotParse("number too large to fit in target type".to_string()) - .with_span(Span::new(21, 26)) - .with_content(Arc::from(file)); + let error = Error::CannotParse { + msg: "number too large to fit in target type".to_string(), + } + .with_span(Span::new(21, 26)) + .with_content(Arc::from(file)); let expected = r#" | @@ -913,9 +972,11 @@ let a: u8 = 65536; let x: u32 = Left( Right(0) );"#; - let error = Error::CannotParse("This span covers the entire file".to_string()) - .with_span(Span::from(file)) - .with_content(Arc::from(file)); + let error = Error::CannotParse { + msg: "This span covers the entire file".to_string(), + } + .with_span(Span::from(file)) + .with_content(Arc::from(file)); let expected = r#" | @@ -932,9 +993,11 @@ let x: u32 = Left( #[test] fn display_with_unicode_separator() { let file = "let a: u8 = 65536;\u{2028}let b: u8 = 0;"; - let error = Error::CannotParse("number too large to fit in target type".to_string()) - .with_span(Span::new(12, 17)) - .with_content(Arc::from(file)); + let error = Error::CannotParse { + msg: "number too large to fit in target type".to_string(), + } + .with_span(Span::new(12, 17)) + .with_content(Arc::from(file)); let expected = r#" | @@ -947,9 +1010,11 @@ let x: u32 = Left( #[test] fn display_span_as_point() { let file = "fn main()"; - let error = Error::Grammar("Error span at (0,0)".to_string()) - .with_span(Span::new(0, 0)) - .with_content(Arc::from(file)); + let error = Error::Grammar { + msg: "Error span at (0,0)".to_string(), + } + .with_span(Span::new(0, 0)) + .with_content(Arc::from(file)); let expected = r#" | @@ -961,9 +1026,11 @@ let x: u32 = Left( #[test] fn display_span_as_point_on_trailing_empty_line() { let file = "fn main(){\n let a:\n"; - let error = Error::CannotParse("eof".to_string()) - .with_span(Span::new(file.len(), file.len())) - .with_content(Arc::from(file)); + let error = Error::CannotParse { + msg: "eof".to_string(), + } + .with_span(Span::new(file.len(), file.len())) + .with_content(Arc::from(file)); let expected = r#" | @@ -977,7 +1044,7 @@ let x: u32 = Left( #[test] fn display_single_line_with_file() { let source = SourceFile::new(std::path::Path::new("src/main.simf"), Arc::from(CONTENT)); - let error = Error::ListBoundPow2(5) + let error = Error::ListBoundPow2 { bound: 5 } .with_span(Span::new(13, 19)) .with_source(source); @@ -992,9 +1059,9 @@ let x: u32 = Left( #[test] fn display_multi_line_with_file() { let source = SourceFile::new(std::path::Path::new("lib/parser.simf"), Arc::from(CONTENT)); - let error = Error::CannotParse( - "Expected value of type `u32`, got `Either, _>`".to_string(), - ) + let error = Error::CannotParse { + msg: "Expected value of type `u32`, got `Either, _>`".to_string(), + } .with_span(Span::new(41, CONTENT.len())) .with_source(source); @@ -1014,9 +1081,11 @@ let x: u32 = Left( std::path::Path::new("tests/integration.simf"), Arc::from(CONTENT), ); - let error = Error::CannotParse("This span covers the entire file".to_string()) - .with_span(Span::from(CONTENT)) - .with_source(source); + let error = Error::CannotParse { + msg: "This span covers the entire file".to_string(), + } + .with_span(Span::from(CONTENT)) + .with_source(source); let expected = r#" --> tests/integration.simf:1:1 diff --git a/src/lexer.rs b/src/lexer.rs index f7505111..06d63adc 100644 --- a/src/lexer.rs +++ b/src/lexer.rs @@ -247,7 +247,9 @@ pub fn lex<'src>(input: &'src str) -> (Option>, Vec Result<&str, RichError> { let parts = self.path(); - parts - .first() - .copied() - .ok_or_else(|| Error::CannotParse("Empty use path".to_string()).with_span(self.span)) + parts.first().copied().ok_or_else(|| { + Error::CannotParse { + msg: "Empty use path".to_string(), + } + .with_span(self.span) + }) } pub fn items(&self) -> &UseItems { @@ -1188,7 +1190,12 @@ where // TODO: we should use information about open delimiter .validate(move |((open_span, content), close_token), _, emit| { if close_token.is_none() { - emit.emit(Error::Grammar(format!("Unclosed delimiter {open}")).with_span(open_span)) + emit.emit( + Error::Grammar { + msg: format!("Unclosed delimiter {open}"), + } + .with_span(open_span), + ) } content }) @@ -1287,13 +1294,15 @@ impl ChumskyParse for AliasedType { let list = just(Token::Ident("List")) .ignore_then(delimited_with_recovery( ty.then_ignore(parse_token_with_recovery(Token::Comma)) - .then(num.clone().validate(|num, e, emit| { + .then(num.clone().validate(|num, e, emit| -> NonZeroPow2Usize { match NonZeroPow2Usize::from_str(num.as_inner()) { Ok(number) => number, Err(err) => { emit.emit( - Error::Grammar(format!("Cannot parse list bound: {err}")) - .with_span(e.span()), + Error::Grammar { + msg: format!("Cannot parse list bound: {err}"), + } + .with_span(e.span()), ); // fallback to default value NonZeroPow2Usize::TWO @@ -1645,14 +1654,16 @@ impl ChumskyParse for CallName { Ok(num) => match NonZeroPow2Usize::new(num) { Some(val) => val, None => { - emit.emit(Error::ListBoundPow2(num).with_span(e.span())); + emit.emit(Error::ListBoundPow2 { bound: num }.with_span(e.span())); NonZeroPow2Usize::TWO } }, Err(_) => { emit.emit( - Error::CannotParse(format!("Invalid number: {}", bound_str)) - .with_span(e.span()), + Error::CannotParse { + msg: format!("Invalid number: {}", bound_str), + } + .with_span(e.span()), ); NonZeroPow2Usize::TWO } @@ -1670,14 +1681,16 @@ impl ChumskyParse for CallName { .validate(|(func, size_str), e, emit| { let size = match size_str.as_inner().parse::() { Ok(0) => { - emit.emit(Error::ArraySizeNonZero(0).with_span(e.span())); + emit.emit(Error::ArraySizeNonZero { size: 0 }.with_span(e.span())); NonZeroUsize::new(1).unwrap() } Ok(n) => NonZeroUsize::new(n).unwrap(), Err(_) => { emit.emit( - Error::CannotParse(format!("Invalid number: {}", size_str)) - .with_span(e.span()), + Error::CannotParse { + msg: format!("Invalid number: {}", size_str), + } + .with_span(e.span()), ); NonZeroUsize::new(1).unwrap() } @@ -1959,9 +1972,10 @@ impl MatchArm { if !is_block && comma.is_none() { emitter.emit( - Error::Grammar( - "Missing ',' after a match arm that isn't block expression".to_string(), - ) + Error::Grammar { + msg: "Missing ',' after a match arm that isn't block expression" + .to_string(), + } .with_span(e.span()), ); } @@ -2028,8 +2042,11 @@ impl Match { (p1, p2) => { emit.emit( - Error::IncompatibleMatchArms(p1.clone(), p2.clone()) - .with_span(e.span()), + Error::IncompatibleMatchArms { + first: p1.clone(), + second: p2.clone(), + } + .with_span(e.span()), ); (first, second) } diff --git a/src/resolution.rs b/src/resolution.rs index 44da2bd5..eea71223 100644 --- a/src/resolution.rs +++ b/src/resolution.rs @@ -209,7 +209,10 @@ impl DependencyMap { } } - Err(Error::UnknownLibrary(drp_name.to_string())).with_span(*use_decl.span()) + Err(Error::UnknownLibrary { + name: drp_name.to_string(), + }) + .with_span(*use_decl.span()) } fn resolve_external_path( @@ -224,14 +227,20 @@ impl DependencyMap { let resolved = Self::build_and_verify_path(&remapping.target, &parts[1..]).map_err(|failed_path| { RichError::new( - Error::ExternalFileNotFound(drp_name.to_string(), failed_path), + Error::ExternalFileNotFound { + lib: drp_name.to_string(), + filename: failed_path, + }, *use_decl.span(), ) })?; if !resolved.starts_with(&remapping.target) { return Err(RichError::new( - Error::ExternalFileNotFound(drp_name.to_string(), resolved.as_path().to_path_buf()), + Error::ExternalFileNotFound { + lib: drp_name.to_string(), + filename: resolved.as_path().to_path_buf(), + }, *use_decl.span(), )); } @@ -250,20 +259,25 @@ impl DependencyMap { ) -> Result { let root = self .get_package_root(current_file) - .ok_or_else(|| { - Error::Internal( - "The 'crate' root path was not configured by the compiler.".to_string(), - ) + .ok_or_else(|| Error::Internal { + msg: "The 'crate' root path was not configured by the compiler.".to_string(), }) .map_err(|e| RichError::new(e, *use_decl.span()))?; let resolved = Self::build_and_verify_path(root, &parts[1..]).map_err(|failed_path| { - RichError::new(Error::FileNotFound(failed_path), *use_decl.span()) + RichError::new( + Error::FileNotFound { + filename: failed_path, + }, + *use_decl.span(), + ) })?; if !resolved.starts_with(root) { return Err(RichError::new( - Error::FileNotFound(resolved.as_path().to_path_buf()), + Error::FileNotFound { + filename: resolved.as_path().to_path_buf(), + }, *use_decl.span(), )); } @@ -283,9 +297,9 @@ impl DependencyMap { if let (Some(curr), Some(res)) = (current_crate, resolved_crate) { if curr == res { - return Err(Error::LocalFileImportedAsExternal( - resolved.as_path().to_path_buf(), - )) + return Err(Error::LocalFileImportedAsExternal { + path: resolved.as_path().to_path_buf(), + }) .with_span(*use_decl.span()); } } @@ -401,7 +415,7 @@ pub(crate) mod tests { assert!(result.is_err()); assert!(matches!( result.unwrap_err().error(), - Error::UnknownLibrary(..) + Error::UnknownLibrary { .. } )); } @@ -459,7 +473,7 @@ pub(crate) mod tests { assert!(matches!( result.unwrap_err().error(), - Error::LocalFileImportedAsExternal(..) + Error::LocalFileImportedAsExternal { .. } )); } @@ -500,7 +514,7 @@ pub(crate) mod tests { assert!(result.is_err()); assert!(matches!( result.unwrap_err().error(), - Error::Internal(msg) if msg.contains("The 'crate' root path was not configured") + Error::Internal{msg} if msg.contains("The 'crate' root path was not configured") )); } diff --git a/src/value.rs b/src/value.rs index 3ca7fdca..ff7ac378 100644 --- a/src/value.rs +++ b/src/value.rs @@ -139,10 +139,14 @@ impl UIntValue { /// Create an integer from a `binary` string and type. pub fn parse_binary(binary: &Binary, ty: UIntType) -> Result { let s = binary.as_inner(); - let bit_len = Pow2Usize::new(s.len()).ok_or(Error::BitStringPow2(s.len()))?; - let bit_ty = UIntType::from_bit_width(bit_len).ok_or(Error::BitStringPow2(s.len()))?; + let bit_len = Pow2Usize::new(s.len()).ok_or(Error::BitStringPow2 { len: s.len() })?; + let bit_ty = + UIntType::from_bit_width(bit_len).ok_or(Error::BitStringPow2 { len: s.len() })?; if ty != bit_ty { - return Err(Error::ExpressionTypeMismatch(ty.into(), bit_ty.into())); + return Err(Error::ExpressionTypeMismatch { + expected: ty.into(), + found: bit_ty.into(), + }); } let byte_len = bit_len.get().div_ceil(8); diff --git a/src/witness.rs b/src/witness.rs index 7d10476e..13ca0820 100644 --- a/src/witness.rs +++ b/src/witness.rs @@ -118,11 +118,11 @@ impl WitnessValues { }; let assigned_ty = self.0[name].ty(); if assigned_ty != declared_ty { - return Err(Error::WitnessTypeMismatch( - name.clone(), - declared_ty.clone(), - assigned_ty.clone(), - )); + return Err(Error::WitnessTypeMismatch { + name: name.clone(), + declared: declared_ty.clone(), + assigned: assigned_ty.clone(), + }); } } @@ -179,11 +179,11 @@ impl Arguments { .get(name) .ok_or_else(|| Error::ArgumentMissing(name.shallow_clone()))?; if !argument.is_of_type(parameter_ty) { - return Err(Error::ArgumentTypeMismatch( - name.clone(), - parameter_ty.clone(), - argument.ty().clone(), - )); + return Err(Error::ArgumentTypeMismatch { + name: name.clone(), + declared: parameter_ty.clone(), + assigned: argument.ty().clone(), + }); } } From 94227613a28cf4ab1c599f867ff6007979acdda6 Mon Sep 17 00:00:00 2001 From: Volodymyr Herashchenko Date: Mon, 25 May 2026 13:22:44 +0300 Subject: [PATCH 4/4] change obvious error variants to named enum --- src/ast.rs | 60 +++++++++++--------- src/compile/mod.rs | 4 +- src/driver/resolve_order.rs | 8 ++- src/error.rs | 108 ++++++++++++++++++++++++------------ src/parse.rs | 4 +- src/pattern.rs | 8 ++- src/value.rs | 12 ++-- src/witness.rs | 10 ++-- 8 files changed, 133 insertions(+), 81 deletions(-) diff --git a/src/ast.rs b/src/ast.rs index 54420109..fca0947f 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -728,7 +728,7 @@ impl Scope { let resolved_type = self .aliases .get(&global_id) - .ok_or_else(|| Error::UndefinedAlias(name.clone()))?; + .ok_or_else(|| Error::UndefinedAlias { name: name.clone() })?; // 3. Fetch the file scope for visibility checking. let file_scope = self @@ -772,7 +772,7 @@ impl Scope { pub fn insert_alias(&mut self, name: AliasName, ty: AliasedType) -> Result<(), Error> { let plug = (name.clone(), self.file_id); if self.aliases.contains_key(&plug) { - return Err(Error::RedefinedAlias(name)); + return Err(Error::RedefinedAlias { name }); } let _ = self.aliases.insert(plug, self.resolve(&ty)?); @@ -810,7 +810,7 @@ impl Scope { } match self.witnesses.entry(name.clone()) { - Entry::Occupied(_) => Err(Error::WitnessReused(name)), + Entry::Occupied(_) => Err(Error::WitnessReused { name }), Entry::Vacant(entry) => { entry.insert(ty); Ok(()) @@ -844,7 +844,7 @@ impl Scope { let func_name_id = (name.clone(), self.file_id); if self.functions.contains_key(&func_name_id) { - return Err(Error::FunctionRedefined(name)); + return Err(Error::FunctionRedefined { name }); } let _ = self.functions.insert(func_name_id, function); @@ -880,7 +880,7 @@ impl Scope { let function = self .functions .get(&global_id) - .ok_or_else(|| Error::FunctionUndefined(name.clone()))?; + .ok_or_else(|| Error::FunctionUndefined { name: name.clone() })?; // TODO: Consider changing it to a better error handler with a source file. let file_scope = self @@ -946,7 +946,10 @@ impl Program { let main = iter.next().ok_or(Error::MainRequired).with_span(from)?; if iter.next().is_some() { - return Err(Error::FunctionRedefined(FunctionName::main())).with_span(from); + return Err(Error::FunctionRedefined { + name: FunctionName::main(), + }) + .with_span(from); } Ok(Self { main, @@ -1166,7 +1169,7 @@ impl AbstractSyntaxTree for SingleExpression { parse::SingleExpressionInner::Decimal(decimal) => { let ty = ty .as_integer() - .ok_or(Error::ExpressionUnexpectedType(ty.clone())) + .ok_or(Error::ExpressionUnexpectedType { ty: ty.clone() }) .with_span(from)?; UIntValue::parse_decimal(decimal, ty) .with_span(from) @@ -1176,7 +1179,7 @@ impl AbstractSyntaxTree for SingleExpression { parse::SingleExpressionInner::Binary(bits) => { let ty = ty .as_integer() - .ok_or(Error::ExpressionUnexpectedType(ty.clone())) + .ok_or(Error::ExpressionUnexpectedType { ty: ty.clone() }) .with_span(from)?; let value = UIntValue::parse_binary(bits, ty).with_span(from)?; SingleExpressionInner::Constant(Value::from(value)) @@ -1200,7 +1203,9 @@ impl AbstractSyntaxTree for SingleExpression { parse::SingleExpressionInner::Variable(identifier) => { let bound_ty = scope .get_variable(identifier) - .ok_or(Error::UndefinedVariable(identifier.clone())) + .ok_or(Error::UndefinedVariable { + identifier: identifier.clone(), + }) .with_span(from)?; if ty != bound_ty { return Err(Error::ExpressionTypeMismatch { @@ -1220,10 +1225,10 @@ impl AbstractSyntaxTree for SingleExpression { parse::SingleExpressionInner::Tuple(tuple) => { let types = ty .as_tuple() - .ok_or(Error::ExpressionUnexpectedType(ty.clone())) + .ok_or(Error::ExpressionUnexpectedType { ty: ty.clone() }) .with_span(from)?; if tuple.len() != types.len() { - return Err(Error::ExpressionUnexpectedType(ty.clone())).with_span(from); + return Err(Error::ExpressionUnexpectedType { ty: ty.clone() }).with_span(from); } tuple .iter() @@ -1235,10 +1240,10 @@ impl AbstractSyntaxTree for SingleExpression { parse::SingleExpressionInner::Array(array) => { let (el_ty, size) = ty .as_array() - .ok_or(Error::ExpressionUnexpectedType(ty.clone())) + .ok_or(Error::ExpressionUnexpectedType { ty: ty.clone() }) .with_span(from)?; if array.len() != size { - return Err(Error::ExpressionUnexpectedType(ty.clone())).with_span(from); + return Err(Error::ExpressionUnexpectedType { ty: ty.clone() }).with_span(from); } array .iter() @@ -1249,10 +1254,10 @@ impl AbstractSyntaxTree for SingleExpression { parse::SingleExpressionInner::List(list) => { let (el_ty, bound) = ty .as_list() - .ok_or(Error::ExpressionUnexpectedType(ty.clone())) + .ok_or(Error::ExpressionUnexpectedType { ty: ty.clone() }) .with_span(from)?; if bound.get() <= list.len() { - return Err(Error::ExpressionUnexpectedType(ty.clone())).with_span(from); + return Err(Error::ExpressionUnexpectedType { ty: ty.clone() }).with_span(from); } list.iter() .map(|e| Expression::analyze(e, el_ty, scope)) @@ -1262,7 +1267,7 @@ impl AbstractSyntaxTree for SingleExpression { parse::SingleExpressionInner::Either(either) => { let (ty_l, ty_r) = ty .as_either() - .ok_or(Error::ExpressionUnexpectedType(ty.clone())) + .ok_or(Error::ExpressionUnexpectedType { ty: ty.clone() }) .with_span(from)?; match either { Either::Left(parse_l) => Expression::analyze(parse_l, ty_l, scope) @@ -1277,7 +1282,7 @@ impl AbstractSyntaxTree for SingleExpression { parse::SingleExpressionInner::Option(maybe_parse) => { let ty = ty .as_option() - .ok_or(Error::ExpressionUnexpectedType(ty.clone())) + .ok_or(Error::ExpressionUnexpectedType { ty: ty.clone() }) .with_span(from)?; match maybe_parse { Some(parse) => { @@ -1356,13 +1361,13 @@ impl AbstractSyntaxTree for Call { .iter() .map(AliasedType::resolve_builtin) .collect::, AliasName>>() - .map_err(Error::UndefinedAlias) + .map_err(|alias| Error::UndefinedAlias { name: alias }) .with_span(from)?; check_argument_types(from.args(), &args_tys).with_span(from)?; let out_ty = jet .target_type() .resolve_builtin() - .map_err(Error::UndefinedAlias) + .map_err(|alias| Error::UndefinedAlias { name: alias }) .with_span(from)?; check_output_type(&out_ty, ty).with_span(from)?; scope.track_call(from, TrackedCallName::Jet); @@ -1532,7 +1537,7 @@ impl AbstractSyntaxTree for CallName { match from.name() { parse::CallName::Jet(name) => match scope.jet_hinter.parse_jet(name.as_inner()) { Some(jet) if !jet.is_disabled() => Ok(Self::Jet(jet)), - _ => Err(Error::JetDoesNotExist(name.clone())).with_span(from), + _ => Err(Error::JetDoesNotExist { name: name.clone() }).with_span(from), }, parse::CallName::UnwrapLeft(right_ty) => scope .resolve(right_ty) @@ -1563,7 +1568,7 @@ impl AbstractSyntaxTree for CallName { // fn f(element: E, accumulator: A) -> A if function.params().len() != 2 || function.params()[1].ty() != function.body().ty() { - Err(Error::FunctionNotFoldable(name.clone())).with_span(from) + Err(Error::FunctionNotFoldable { name: name.clone() }).with_span(from) } else { Ok(Self::ArrayFold(function, *size)) } @@ -1574,7 +1579,7 @@ impl AbstractSyntaxTree for CallName { // fn f(element: E, accumulator: A) -> A if function.params().len() != 2 || function.params()[1].ty() != function.body().ty() { - Err(Error::FunctionNotFoldable(name.clone())).with_span(from) + Err(Error::FunctionNotFoldable { name: name.clone() }).with_span(from) } else { Ok(Self::Fold(function, *bound)) } @@ -1586,12 +1591,13 @@ impl AbstractSyntaxTree for CallName { // where // N is a power of two if function.params().len() != 3 { - return Err(Error::FunctionNotLoopable(name.clone())).with_span(from); + return Err(Error::FunctionNotLoopable { name: name.clone() }).with_span(from); } match function.body().ty().as_either() { Some((_, out_r)) if out_r == function.params().first().unwrap().ty() => {} _ => { - return Err(Error::FunctionNotLoopable(name.clone())).with_span(from); + return Err(Error::FunctionNotLoopable { name: name.clone() }) + .with_span(from); } } // Disable loops for u32 or higher since no one will want to run @@ -1605,7 +1611,7 @@ impl AbstractSyntaxTree for CallName { | UIntType::U8 | UIntType::U16), ) => Ok(Self::ForWhile(function, int_ty.bit_width())), - _ => Err(Error::FunctionNotLoopable(name.clone())).with_span(from), + _ => Err(Error::FunctionNotLoopable { name: name.clone() }).with_span(from), } } } @@ -1686,7 +1692,9 @@ impl AbstractSyntaxTree for ModuleAssignment { let ty_expr = scope.resolve(from.ty()).with_span(from)?; let expression = Expression::analyze(from.expression(), &ty_expr, scope)?; let value = Value::from_const_expr(&expression) - .ok_or(Error::ExpressionUnexpectedType(ty_expr.clone())) + .ok_or(Error::ExpressionUnexpectedType { + ty: ty_expr.clone(), + }) .with_span(from.expression())?; Ok(Self { diff --git a/src/compile/mod.rs b/src/compile/mod.rs index e3e3812d..ad3a44a8 100644 --- a/src/compile/mod.rs +++ b/src/compile/mod.rs @@ -320,7 +320,9 @@ impl SingleExpression { } SingleExpressionInner::Variable(identifier) => scope .get(&BasePattern::Identifier(identifier.clone())) - .ok_or(Error::UndefinedVariable(identifier.clone())) + .ok_or(Error::UndefinedVariable { + identifier: identifier.clone(), + }) .with_span(self)?, SingleExpressionInner::Expression(expr) => expr.compile(scope)?, SingleExpressionInner::Tuple(elements) | SingleExpressionInner::Array(elements) => { diff --git a/src/driver/resolve_order.rs b/src/driver/resolve_order.rs index 3a5524b7..ad5463d5 100644 --- a/src/driver/resolve_order.rs +++ b/src/driver/resolve_order.rs @@ -363,7 +363,9 @@ impl DependencyGraph { // 5. Check for collisions using `namespace` fields if namespace.registry.direct_targets.contains_key(&local_id) { return Err(RichError::new( - Error::DuplicateAlias(local_symbol.to_string()), + Error::DuplicateAlias { + name: local_symbol.to_string(), + }, span, )); } @@ -420,7 +422,7 @@ fn register_type_alias( if tracker.memo.contains(&local_id) { return Err(RichError::new( - Error::RedefinedAlias(name.clone()), + Error::RedefinedAlias { name: name.clone() }, *item.span(), )); } @@ -446,7 +448,7 @@ fn register_function( if tracker.memo.contains(&local_id) { return Err(RichError::new( - Error::FunctionRedefined(name.clone()), + Error::FunctionRedefined { name: name.clone() }, *item.span(), )); } diff --git a/src/error.rs b/src/error.rs index 42b16bc4..83420973 100644 --- a/src/error.rs +++ b/src/error.rs @@ -533,7 +533,9 @@ pub enum Error { ParseCrateInt { source: crate::num::ParseIntError, }, - JetDoesNotExist(JetName), + JetDoesNotExist { + name: JetName, + }, InvalidCast { source: ResolvedType, target: ResolvedType, @@ -563,37 +565,69 @@ pub enum Error { MainOutOfEntryFile, MainCannotBePublic, MainCannotBeAlias, - FunctionRedefined(FunctionName), - FunctionUndefined(FunctionName), + FunctionRedefined { + name: FunctionName, + }, + FunctionUndefined { + name: FunctionName, + }, InvalidNumberOfArguments { expected: usize, found: usize, }, - FunctionNotFoldable(FunctionName), - FunctionNotLoopable(FunctionName), - ExpressionUnexpectedType(ResolvedType), + FunctionNotFoldable { + name: FunctionName, + }, + FunctionNotLoopable { + name: FunctionName, + }, + ExpressionUnexpectedType { + ty: ResolvedType, + }, ExpressionTypeMismatch { expected: ResolvedType, found: ResolvedType, }, ExpressionNotConstant, - IntegerOutOfBounds(UIntType), - UndefinedVariable(Identifier), - RedefinedAlias(AliasName), - RedefinedAliasAsBuiltin(AliasName), - UndefinedAlias(AliasName), - DuplicateAlias(String), - VariableReuseInPattern(Identifier), - WitnessReused(WitnessName), + IntegerOutOfBounds { + ty: UIntType, + }, + UndefinedVariable { + identifier: Identifier, + }, + RedefinedAlias { + name: AliasName, + }, + RedefinedAliasAsBuiltin { + name: AliasName, + }, + UndefinedAlias { + name: AliasName, + }, + DuplicateAlias { + name: String, + }, + VariableReuseInPattern { + identifier: Identifier, + }, + WitnessReused { + name: WitnessName, + }, WitnessTypeMismatch { name: WitnessName, declared: ResolvedType, assigned: ResolvedType, }, - WitnessReassigned(WitnessName), + WitnessReassigned { + name: WitnessName, + }, WitnessOutsideMain, - ModuleRedefined(ModuleName), - ArgumentMissing(WitnessName), + ModuleRedefined { + name: ModuleName, + }, + ArgumentMissing { + name: WitnessName, + }, ArgumentTypeMismatch { name: WitnessName, declared: ResolvedType, @@ -690,7 +724,7 @@ impl fmt::Display for Error { "Failed to compile to Simplicity" ), Error::ParseInt { .. } | Error::ParseCrateInt { .. } => write!(f, "Integer parsing error"), - Error::JetDoesNotExist(name) => write!( + Error::JetDoesNotExist { name } => write!( f, "Jet `{name}` does not exist" ), @@ -722,11 +756,11 @@ impl fmt::Display for Error { f, "Main function cannot be alias", ), - Error::FunctionRedefined(name) => write!( + Error::FunctionRedefined { name } => write!( f, "Function `{name}` was defined multiple times" ), - Error::FunctionUndefined(name) => write!( + Error::FunctionUndefined { name } => write!( f, "Function `{name}` was called but not defined" ), @@ -746,15 +780,15 @@ impl fmt::Display for Error { f, "Expected {expected} arguments, found {found} arguments" ), - Error::FunctionNotFoldable(name) => write!( + Error::FunctionNotFoldable { name } => write!( f, "Expected a signature like `fn {name}(element: E, accumulator: A) -> A` for a fold" ), - Error::FunctionNotLoopable(name) => write!( + Error::FunctionNotLoopable { name } => write!( f, "Expected a signature like `fn {name}(accumulator: A, context: C, counter u{{1,2,4,8,16}}) -> Either` for a for-while loop" ), - Error::ExpressionUnexpectedType(ty) => write!( + Error::ExpressionUnexpectedType { ty } => write!( f, "Expected expression of type `{ty}`; found something else" ), @@ -766,35 +800,35 @@ impl fmt::Display for Error { f, "Expression cannot be evaluated at compile time" ), - Error::IntegerOutOfBounds(ty) => write!( + Error::IntegerOutOfBounds { ty } => write!( f, "Value is out of bounds for type `{ty}`" ), - Error::UndefinedVariable(identifier) => write!( + Error::UndefinedVariable { identifier } => write!( f, "Variable `{identifier}` is not defined" ), - Error::RedefinedAlias(identifier) => write!( + Error::RedefinedAlias { name } => write!( f, - "Type alias `{identifier}` was defined multiple times" + "Type alias `{name}` was defined multiple times" ), - Error::RedefinedAliasAsBuiltin(identifier) => write!( + Error::RedefinedAliasAsBuiltin { name } => write!( f, - "Type alias `{identifier}` is already exists as built-in alias" + "Type alias `{name}` is already exists as built-in alias" ), - Error::UndefinedAlias(identifier) => write!( + Error::UndefinedAlias { name } => write!( f, - "Type alias `{identifier}` is not defined" + "Type alias `{name}` is not defined" ), - Error::DuplicateAlias(name) => write!( + Error::DuplicateAlias { name } => write!( f, "The alias `{name}` was defined multiple times" ), - Error::VariableReuseInPattern(identifier) => write!( + Error::VariableReuseInPattern { identifier } => write!( f, "Variable `{identifier}` is used twice in the pattern" ), - Error::WitnessReused(name) => write!( + Error::WitnessReused { name } => write!( f, "Witness `{name}` has been used before somewhere in the program" ), @@ -802,7 +836,7 @@ impl fmt::Display for Error { f, "Witness `{name}` was declared with type `{declared}` but its assigned value is of type `{assigned}`" ), - Error::WitnessReassigned(name) => write!( + Error::WitnessReassigned { name } => write!( f, "Witness `{name}` has already been assigned a value" ), @@ -810,11 +844,11 @@ impl fmt::Display for Error { f, "Witness expressions are not allowed outside the `main` function" ), - Error::ModuleRedefined(name) => write!( + Error::ModuleRedefined { name } => write!( f, "Module `{name}` is defined twice" ), - Error::ArgumentMissing(name) => write!( + Error::ArgumentMissing { name } => write!( f, "Parameter `{name}` is missing an argument" ), diff --git a/src/parse.rs b/src/parse.rs index 2de0782b..42b5dac2 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -1755,7 +1755,9 @@ impl ChumskyParse for TypeAlias { }; if known_type.is_some() { - emit.emit(Error::RedefinedAliasAsBuiltin(name.clone()).with_span(e.span())); + emit.emit( + Error::RedefinedAliasAsBuiltin { name: name.clone() }.with_span(e.span()), + ); } name }) diff --git a/src/pattern.rs b/src/pattern.rs index 1cd6e4d2..b54726ad 100644 --- a/src/pattern.rs +++ b/src/pattern.rs @@ -52,7 +52,11 @@ impl Pattern { while let Some((pattern, ty)) = stack.pop() { match (pattern, ty.as_inner()) { (Pattern::Identifier(i), _) => match output.entry(i.clone()) { - Entry::Occupied(..) => return Err(Error::VariableReuseInPattern(i.clone())), + Entry::Occupied(..) => { + return Err(Error::VariableReuseInPattern { + identifier: i.clone(), + }) + } Entry::Vacant(entry) => { entry.insert(ty.clone()); } @@ -64,7 +68,7 @@ impl Pattern { (Pattern::Array(pats), TypeInner::Array(ty, size)) if pats.len() == *size => { stack.extend(pats.iter().zip(std::iter::repeat(ty.as_ref()))); } - _ => return Err(Error::ExpressionUnexpectedType(ty.clone())), + _ => return Err(Error::ExpressionUnexpectedType { ty: ty.clone() }), } } Ok(output) diff --git a/src/value.rs b/src/value.rs index ff7ac378..1ccb38bd 100644 --- a/src/value.rs +++ b/src/value.rs @@ -92,7 +92,7 @@ impl UIntValue { pub const fn u1(value: u8) -> Result { match value { 0 | 1 => Ok(Self::U1(value)), - _ => Err(Error::IntegerOutOfBounds(UIntType::U1)), + _ => Err(Error::IntegerOutOfBounds { ty: UIntType::U1 }), } } @@ -104,7 +104,7 @@ impl UIntValue { pub const fn u2(value: u8) -> Result { match value { 0..=3 => Ok(Self::U2(value)), - _ => Err(Error::IntegerOutOfBounds(UIntType::U2)), + _ => Err(Error::IntegerOutOfBounds { ty: UIntType::U2 }), } } @@ -116,7 +116,7 @@ impl UIntValue { pub const fn u4(value: u8) -> Result { match value { 0..=15 => Ok(Self::U4(value)), - _ => Err(Error::IntegerOutOfBounds(UIntType::U4)), + _ => Err(Error::IntegerOutOfBounds { ty: UIntType::U4 }), } } @@ -632,11 +632,11 @@ impl Value { let expected_byte_len = match ty.as_inner() { TypeInner::UInt(int) => int.byte_width(), TypeInner::Array(inner, len) if inner.as_integer() == Some(UIntType::U8) => *len, - _ => return Err(Error::ExpressionUnexpectedType(ty.clone())), + _ => return Err(Error::ExpressionUnexpectedType { ty: ty.clone() }), }; let s = hexadecimal.as_inner(); if s.len() % 2 != 0 || s.len() != expected_byte_len * 2 { - return Err(Error::ExpressionUnexpectedType(ty.clone())); + return Err(Error::ExpressionUnexpectedType { ty: ty.clone() }); } let bytes = Vec::::from_hex(s).expect("valid chars and valid length"); let ret = match ty.as_inner() { @@ -790,7 +790,7 @@ impl Value { let parse_expr = parse::Expression::parse_from_str(s)?; let ast_expr = ast::Expression::analyze_const(&parse_expr, ty)?; Self::from_const_expr(&ast_expr) - .ok_or(Error::ExpressionUnexpectedType(ty.clone())) + .ok_or(Error::ExpressionUnexpectedType { ty: ty.clone() }) .with_span(s) } } diff --git a/src/witness.rs b/src/witness.rs index 13ca0820..3fd878d4 100644 --- a/src/witness.rs +++ b/src/witness.rs @@ -135,7 +135,7 @@ impl ParseFromStr for ResolvedType { let aliased = AliasedType::parse_from_str(s)?; aliased .resolve_builtin() - .map_err(Error::UndefinedAlias) + .map_err(|name| Error::UndefinedAlias { name }) .with_span(s) .with_content(s) } @@ -175,9 +175,9 @@ impl Arguments { /// Arguments without a corresponding parameter are ignored. pub fn is_consistent(&self, parameters: &Parameters) -> Result<(), Error> { for (name, parameter_ty) in parameters.iter() { - let argument = self - .get(name) - .ok_or_else(|| Error::ArgumentMissing(name.shallow_clone()))?; + let argument = self.get(name).ok_or_else(|| Error::ArgumentMissing { + name: name.shallow_clone(), + })?; if !argument.is_of_type(parameter_ty) { return Err(Error::ArgumentTypeMismatch { name: name.clone(), @@ -237,7 +237,7 @@ mod tests { .map_err(Error::from) { Ok(_) => panic!("Witness reuse was falsely accepted"), - Err(Error::WitnessReused(..)) => {} + Err(Error::WitnessReused { .. }) => {} Err(error) => panic!("Unexpected error: {error}"), } }