diff --git a/CHANGELOG.md b/CHANGELOG.md index 08fc72b3..b68a22d4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ Bug fixes * Invalid property or method access such as `a.b::c.d` or `a.b::func()` no longer panics but properly returns a syntax error. * `Scope::is_constant` now returns the correct value. +* Exporting a variable that contains a local function pointer (including anonymous function or closure) now raises a runtime error. Enhancements ------------ diff --git a/src/module/mod.rs b/src/module/mod.rs index 8139a155..71768911 100644 --- a/src/module/mod.rs +++ b/src/module/mod.rs @@ -1720,7 +1720,27 @@ impl Module { result?; // Variables with an alias left in the scope become module variables - for (.., value, mut aliases) in scope { + for (name, value, mut aliases) in scope { + // It is an error to export function pointers that refer to encapsulated functions + if let Some(fn_ptr) = value.downcast_ref::() { + if ast.iter_fn_def().any(|f| f.name == fn_ptr.fn_name()) { + return Err(crate::ERR::ErrorMismatchDataType( + "".to_string(), + if fn_ptr.is_anonymous() { + format!("cannot export closure in variable {}", name) + } else { + format!( + "cannot export function pointer to local function '{}' in variable {}", + fn_ptr.fn_name(), + name + ) + }, + crate::Position::NONE, + ) + .into()); + } + } + match aliases.len() { 0 => (), 1 => { diff --git a/src/types/error.rs b/src/types/error.rs index 9eaf7907..10dfe851 100644 --- a/src/types/error.rs +++ b/src/types/error.rs @@ -145,9 +145,9 @@ impl fmt::Display for EvalAltResult { } Self::ErrorInModule(s, err, ..) if s.is_empty() => { - write!(f, "Error in module: {}", err)? + write!(f, "Error in module > {}", err)? } - Self::ErrorInModule(s, err, ..) => write!(f, "Error in module {}: {}", s, err)?, + Self::ErrorInModule(s, err, ..) => write!(f, "Error in module '{}' > {}", s, err)?, Self::ErrorVariableExists(s, ..) => write!(f, "Variable is already defined: {}", s)?, Self::ErrorForbiddenVariable(s, ..) => write!(f, "Forbidden variable name: {}", s)?, @@ -180,15 +180,15 @@ impl fmt::Display for EvalAltResult { Self::ErrorRuntime(d, ..) => write!(f, "Runtime error: {}", d)?, Self::ErrorAssignmentToConstant(s, ..) => write!(f, "Cannot modify constant: {}", s)?, - Self::ErrorMismatchOutputType(s, r, ..) => match (r.as_str(), s.as_str()) { - ("", s) => write!(f, "Output type is incorrect, expecting {}", s), - (r, "") => write!(f, "Output type is incorrect: {}", r), - (r, s) => write!(f, "Output type is incorrect: {} (expecting {})", r, s), + Self::ErrorMismatchOutputType(e, a, ..) => match (a.as_str(), e.as_str()) { + ("", e) => write!(f, "Output type is incorrect, expecting {}", e), + (a, "") => write!(f, "Output type is incorrect: {}", a), + (a, e) => write!(f, "Output type is incorrect: {} (expecting {})", a, e), }?, - Self::ErrorMismatchDataType(s, r, ..) => match (r.as_str(), s.as_str()) { - ("", s) => write!(f, "Data type is incorrect, expecting {}", s), - (r, "") => write!(f, "Data type is incorrect: {}", r), - (r, s) => write!(f, "Data type is incorrect: {} (expecting {})", r, s), + Self::ErrorMismatchDataType(e, a, ..) => match (a.as_str(), e.as_str()) { + ("", e) => write!(f, "Data type is incorrect, expecting {}", e), + (a, "") => write!(f, "Data type is incorrect: {}", a), + (a, e) => write!(f, "Data type is incorrect: {} (expecting {})", a, e), }?, Self::ErrorArithmetic(s, ..) => match s.as_str() { "" => f.write_str("Arithmetic error"),