Raise error when exporting local anonymous function.

This commit is contained in:
Stephen Chung 2022-02-24 11:24:31 +08:00
parent 73f10b8adc
commit 39ef766bf1
3 changed files with 32 additions and 11 deletions

View File

@ -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. * 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. * `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 Enhancements
------------ ------------

View File

@ -1720,7 +1720,27 @@ impl Module {
result?; result?;
// Variables with an alias left in the scope become module variables // 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::<crate::FnPtr>() {
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() { match aliases.len() {
0 => (), 0 => (),
1 => { 1 => {

View File

@ -145,9 +145,9 @@ impl fmt::Display for EvalAltResult {
} }
Self::ErrorInModule(s, err, ..) if s.is_empty() => { 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::ErrorVariableExists(s, ..) => write!(f, "Variable is already defined: {}", s)?,
Self::ErrorForbiddenVariable(s, ..) => write!(f, "Forbidden variable name: {}", 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::ErrorRuntime(d, ..) => write!(f, "Runtime error: {}", d)?,
Self::ErrorAssignmentToConstant(s, ..) => write!(f, "Cannot modify constant: {}", s)?, Self::ErrorAssignmentToConstant(s, ..) => write!(f, "Cannot modify constant: {}", s)?,
Self::ErrorMismatchOutputType(s, r, ..) => match (r.as_str(), s.as_str()) { Self::ErrorMismatchOutputType(e, a, ..) => match (a.as_str(), e.as_str()) {
("", s) => write!(f, "Output type is incorrect, expecting {}", s), ("", e) => write!(f, "Output type is incorrect, expecting {}", e),
(r, "") => write!(f, "Output type is incorrect: {}", r), (a, "") => write!(f, "Output type is incorrect: {}", a),
(r, s) => write!(f, "Output type is incorrect: {} (expecting {})", r, s), (a, e) => write!(f, "Output type is incorrect: {} (expecting {})", a, e),
}?, }?,
Self::ErrorMismatchDataType(s, r, ..) => match (r.as_str(), s.as_str()) { Self::ErrorMismatchDataType(e, a, ..) => match (a.as_str(), e.as_str()) {
("", s) => write!(f, "Data type is incorrect, expecting {}", s), ("", e) => write!(f, "Data type is incorrect, expecting {}", e),
(r, "") => write!(f, "Data type is incorrect: {}", r), (a, "") => write!(f, "Data type is incorrect: {}", a),
(r, s) => write!(f, "Data type is incorrect: {} (expecting {})", r, s), (a, e) => write!(f, "Data type is incorrect: {} (expecting {})", a, e),
}?, }?,
Self::ErrorArithmetic(s, ..) => match s.as_str() { Self::ErrorArithmetic(s, ..) => match s.as_str() {
"" => f.write_str("Arithmetic error"), "" => f.write_str("Arithmetic error"),