Fix switch of non-hashable value.
This commit is contained in:
parent
0fa3968b74
commit
4c5ea8decc
@ -11,6 +11,7 @@ Bug fixes
|
|||||||
* Fixed error types in `EvalAltResult::ErrorMismatchDataType` which were swapped.
|
* Fixed error types in `EvalAltResult::ErrorMismatchDataType` which were swapped.
|
||||||
* Some expressions involving shared variables now work properly, for example `x in shared_value`, `return shared_value`, `obj.field = shared_value` etc. Previously, the resultant value is still shared which is counter-intuitive.
|
* Some expressions involving shared variables now work properly, for example `x in shared_value`, `return shared_value`, `obj.field = shared_value` etc. Previously, the resultant value is still shared which is counter-intuitive.
|
||||||
* Potential overflow panics in `range(from, to, step)` is fixed.
|
* Potential overflow panics in `range(from, to, step)` is fixed.
|
||||||
|
* `switch` statements no longer panic on custom types.
|
||||||
|
|
||||||
Breaking changes
|
Breaking changes
|
||||||
----------------
|
----------------
|
||||||
|
@ -414,7 +414,7 @@ impl Hash for Dynamic {
|
|||||||
#[cfg(feature = "sync")]
|
#[cfg(feature = "sync")]
|
||||||
Union::Shared(cell, _) => (*cell.read().unwrap()).hash(state),
|
Union::Shared(cell, _) => (*cell.read().unwrap()).hash(state),
|
||||||
|
|
||||||
_ => unimplemented!(),
|
_ => unimplemented!("{} cannot be hashed", self.type_name()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -761,6 +761,32 @@ impl Dynamic {
|
|||||||
_ => self.access_mode().is_read_only(),
|
_ => self.access_mode().is_read_only(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/// Can this [`Dynamic`] be hashed?
|
||||||
|
pub(crate) fn is_hashable(&self) -> bool {
|
||||||
|
match &self.0 {
|
||||||
|
Union::Unit(_, _)
|
||||||
|
| Union::Bool(_, _)
|
||||||
|
| Union::Str(_, _)
|
||||||
|
| Union::Char(_, _)
|
||||||
|
| Union::Int(_, _) => true,
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
Union::Float(_, _) => true,
|
||||||
|
#[cfg(not(feature = "no_index"))]
|
||||||
|
Union::Array(_, _) => true,
|
||||||
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
Union::Map(_, _) => true,
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_closure"))]
|
||||||
|
#[cfg(not(feature = "sync"))]
|
||||||
|
Union::Shared(cell, _) => cell.borrow().is_hashable(),
|
||||||
|
#[cfg(not(feature = "no_closure"))]
|
||||||
|
#[cfg(feature = "sync")]
|
||||||
|
Union::Shared(cell, _) => cell.read().unwrap().is_hashable(),
|
||||||
|
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
/// Create a [`Dynamic`] from any type. A [`Dynamic`] value is simply returned as is.
|
/// Create a [`Dynamic`] from any type. A [`Dynamic`] value is simply returned as is.
|
||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
|
@ -2111,18 +2111,29 @@ impl Engine {
|
|||||||
Stmt::Switch(match_expr, x, _) => {
|
Stmt::Switch(match_expr, x, _) => {
|
||||||
let (table, def_stmt) = x.as_ref();
|
let (table, def_stmt) = x.as_ref();
|
||||||
|
|
||||||
|
let value = self.eval_expr(scope, mods, state, lib, this_ptr, match_expr, level)?;
|
||||||
|
|
||||||
|
if value.is_hashable() {
|
||||||
let hasher = &mut get_hasher();
|
let hasher = &mut get_hasher();
|
||||||
self.eval_expr(scope, mods, state, lib, this_ptr, match_expr, level)?
|
value.hash(hasher);
|
||||||
.hash(hasher);
|
|
||||||
let hash = hasher.finish();
|
let hash = hasher.finish();
|
||||||
|
|
||||||
if let Some(stmt) = table.get(&hash) {
|
table
|
||||||
self.eval_stmt(scope, mods, state, lib, this_ptr, stmt, level)
|
.get(&hash)
|
||||||
} else if let Some(def_stmt) = def_stmt {
|
.map(|stmt| self.eval_stmt(scope, mods, state, lib, this_ptr, stmt, level))
|
||||||
self.eval_stmt(scope, mods, state, lib, this_ptr, def_stmt, level)
|
|
||||||
} else {
|
} else {
|
||||||
Ok(Dynamic::UNIT)
|
// Non-hashable values never match any specific clause
|
||||||
|
None
|
||||||
}
|
}
|
||||||
|
.unwrap_or_else(|| {
|
||||||
|
// Default match clause
|
||||||
|
def_stmt.as_ref().map_or_else(
|
||||||
|
|| Ok(Dynamic::UNIT),
|
||||||
|
|def_stmt| {
|
||||||
|
self.eval_stmt(scope, mods, state, lib, this_ptr, def_stmt, level)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// While loop
|
// While loop
|
||||||
|
@ -18,6 +18,10 @@ fn test_switch() -> Result<(), Box<EvalAltResult>> {
|
|||||||
engine.eval_with_scope::<()>(&mut scope, "switch x { 1 => 123, 2 => 'a' }")?,
|
engine.eval_with_scope::<()>(&mut scope, "switch x { 1 => 123, 2 => 'a' }")?,
|
||||||
()
|
()
|
||||||
);
|
);
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval::<INT>("let x = timestamp(); switch x { 1 => 123, _ => 42 }")?,
|
||||||
|
42
|
||||||
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
engine.eval_with_scope::<INT>(
|
engine.eval_with_scope::<INT>(
|
||||||
&mut scope,
|
&mut scope,
|
||||||
|
Loading…
Reference in New Issue
Block a user