diff --git a/src/api/call_fn.rs b/src/api/call_fn.rs index b467e54d..42f418e4 100644 --- a/src/api/call_fn.rs +++ b/src/api/call_fn.rs @@ -250,7 +250,9 @@ impl Engine { ) -> RhaiResult { let statements = ast.statements(); let lib = &[AsRef::::as_ref(ast).clone()]; - let mut this_ptr = this_ptr; + + let mut no_this_ptr = Dynamic::NULL; + let mut this_ptr = this_ptr.unwrap_or(&mut no_this_ptr); let orig_scope_len = scope.len(); diff --git a/src/api/eval.rs b/src/api/eval.rs index 28bcdcbd..8770996d 100644 --- a/src/api/eval.rs +++ b/src/api/eval.rs @@ -198,8 +198,10 @@ impl Engine { #[cfg(not(feature = "no_function"))] AsRef::::as_ref(ast).clone(), ]; + let mut this = Dynamic::NULL; let node = &crate::ast::Stmt::Noop(Position::NONE); - self.run_debugger(global, caches, lib, 0, scope, &mut None, node)?; + + self.run_debugger(global, caches, lib, 0, scope, &mut this, node)?; } let typ = self.map_type_name(result.type_name()); diff --git a/src/api/run.rs b/src/api/run.rs index 90c6abf2..f1c25bb4 100644 --- a/src/api/run.rs +++ b/src/api/run.rs @@ -141,8 +141,9 @@ impl Engine { #[cfg(not(feature = "no_function"))] AsRef::::as_ref(ast).clone(), ]; + let mut this = crate::Dynamic::NULL; let node = &crate::ast::Stmt::Noop(crate::Position::NONE); - self.run_debugger(global, caches, lib, 0, scope, &mut None, node)?; + self.run_debugger(global, caches, lib, 0, scope, &mut this, node)?; } Ok(()) diff --git a/src/eval/chaining.rs b/src/eval/chaining.rs index 021dfdc3..a37fd16e 100644 --- a/src/eval/chaining.rs +++ b/src/eval/chaining.rs @@ -45,7 +45,7 @@ impl Engine { caches: &mut Caches, lib: &[SharedModule], level: usize, - this_ptr: &mut Option<&mut Dynamic>, + this_ptr: &mut Dynamic, target: &mut Target, root: (&str, Position), _parent: &Expr, @@ -573,7 +573,7 @@ impl Engine { lib: &[SharedModule], level: usize, scope: &mut Scope, - this_ptr: &mut Option<&mut Dynamic>, + this_ptr: &mut Dynamic, expr: &Expr, new_val: &mut Option<(Dynamic, &OpAssignment)>, ) -> RhaiResult { @@ -630,9 +630,10 @@ impl Engine { let obj_ptr = &mut target; let root = (x.3.as_str(), *var_pos); + let mut this = Dynamic::NULL; self.eval_dot_index_chain_helper( - global, caches, lib, level, &mut None, obj_ptr, root, expr, options, rhs, + global, caches, lib, level, &mut this, obj_ptr, root, expr, options, rhs, idx_values, chain_type, new_val, ) } @@ -664,7 +665,7 @@ impl Engine { lib: &[SharedModule], level: usize, scope: &mut Scope, - this_ptr: &mut Option<&mut Dynamic>, + this_ptr: &mut Dynamic, expr: &Expr, parent_options: ASTFlags, parent_chain_type: ChainType, diff --git a/src/eval/debugger.rs b/src/eval/debugger.rs index 8c2d94e5..124b3547 100644 --- a/src/eval/debugger.rs +++ b/src/eval/debugger.rs @@ -416,7 +416,7 @@ impl Engine { lib: &[SharedModule], level: usize, scope: &mut Scope, - this_ptr: &mut Option<&mut Dynamic>, + this_ptr: &mut Dynamic, node: impl Into>, ) -> RhaiResultOf<()> { if self.debugger.is_some() { @@ -443,7 +443,7 @@ impl Engine { lib: &[SharedModule], level: usize, scope: &mut Scope, - this_ptr: &mut Option<&mut Dynamic>, + this_ptr: &mut Dynamic, node: impl Into>, ) -> RhaiResultOf> { if self.debugger.is_some() { @@ -466,7 +466,7 @@ impl Engine { lib: &[SharedModule], level: usize, scope: &mut Scope, - this_ptr: &mut Option<&mut Dynamic>, + this_ptr: &mut Dynamic, node: impl Into>, ) -> RhaiResultOf> { let node = node.into(); @@ -513,7 +513,7 @@ impl Engine { lib: &[SharedModule], level: usize, scope: &mut Scope, - this_ptr: &mut Option<&mut Dynamic>, + this_ptr: &mut Dynamic, node: ASTNode<'a>, event: DebuggerEvent, ) -> Result, Box> { diff --git a/src/eval/eval_context.rs b/src/eval/eval_context.rs index dc50a63c..ed5d6193 100644 --- a/src/eval/eval_context.rs +++ b/src/eval/eval_context.rs @@ -8,7 +8,7 @@ use std::prelude::v1::*; /// Context of a script evaluation process. #[derive(Debug)] #[allow(dead_code)] -pub struct EvalContext<'a, 's, 'ps, 'g, 'c, 't, 'pt> { +pub struct EvalContext<'a, 's, 'ps, 'g, 'c, 't> { /// The current [`Engine`]. engine: &'a Engine, /// The current [`Scope`]. @@ -20,12 +20,12 @@ pub struct EvalContext<'a, 's, 'ps, 'g, 'c, 't, 'pt> { /// The current stack of imported [modules][Module]. lib: &'a [SharedModule], /// The current bound `this` pointer, if any. - this_ptr: &'t mut Option<&'pt mut Dynamic>, + this_ptr: &'t mut Dynamic, /// The current nesting level of function calls. level: usize, } -impl<'a, 's, 'ps, 'g, 'c, 't, 'pt> EvalContext<'a, 's, 'ps, 'g, 'c, 't, 'pt> { +impl<'a, 's, 'ps, 'g, 'c, 't> EvalContext<'a, 's, 'ps, 'g, 'c, 't> { /// Create a new [`EvalContext`]. #[inline(always)] #[must_use] @@ -36,7 +36,7 @@ impl<'a, 's, 'ps, 'g, 'c, 't, 'pt> EvalContext<'a, 's, 'ps, 'g, 'c, 't, 'pt> { lib: &'a [SharedModule], level: usize, scope: &'s mut Scope<'ps>, - this_ptr: &'t mut Option<&'pt mut Dynamic>, + this_ptr: &'t mut Dynamic, ) -> Self { Self { engine, @@ -104,8 +104,8 @@ impl<'a, 's, 'ps, 'g, 'c, 't, 'pt> EvalContext<'a, 's, 'ps, 'g, 'c, 't, 'pt> { #[cfg(feature = "internals")] #[inline(always)] #[must_use] - pub fn global_runtime_state_mut(&mut self) -> &mut &'g mut GlobalRuntimeState { - &mut self.global + pub fn global_runtime_state_mut(&mut self) -> &mut GlobalRuntimeState { + self.global } /// Get an iterator over the namespaces containing definition of all script-defined functions. #[inline] @@ -121,16 +121,24 @@ impl<'a, 's, 'ps, 'g, 'c, 't, 'pt> EvalContext<'a, 's, 'ps, 'g, 'c, 't, 'pt> { self.lib } /// The current bound `this` pointer, if any. - #[inline(always)] + #[inline] #[must_use] pub fn this_ptr(&self) -> Option<&Dynamic> { - self.this_ptr.as_ref().map(|v| &**v) + if self.this_ptr.is_null() { + None + } else { + Some(self.this_ptr) + } } /// Mutable reference to the current bound `this` pointer, if any. - #[inline(always)] + #[inline] #[must_use] - pub fn this_ptr_mut(&mut self) -> &mut Option<&'pt mut Dynamic> { - self.this_ptr + pub fn this_ptr_mut(&mut self) -> Option<&mut Dynamic> { + if self.this_ptr.is_null() { + None + } else { + Some(self.this_ptr) + } } /// The current nesting level of function calls. #[inline(always)] diff --git a/src/eval/expr.rs b/src/eval/expr.rs index 89f0d4a7..55467124 100644 --- a/src/eval/expr.rs +++ b/src/eval/expr.rs @@ -54,7 +54,7 @@ impl Engine { lib: &[SharedModule], level: usize, scope: &'s mut Scope, - this_ptr: &'s mut Option<&mut Dynamic>, + this_ptr: &'s mut Dynamic, expr: &Expr, ) -> RhaiResultOf<(Target<'s>, Position)> { match expr { @@ -140,7 +140,7 @@ impl Engine { lib: &[SharedModule], level: usize, scope: &'s mut Scope, - this_ptr: &'s mut Option<&mut Dynamic>, + this_ptr: &'s mut Dynamic, expr: &Expr, ) -> RhaiResultOf<(Target<'s>, Position)> { // Make sure that the pointer indirection is taken only when absolutely necessary. @@ -148,10 +148,11 @@ impl Engine { let (index, var_pos) = match expr { // Check if the variable is `this` Expr::Variable(v, None, pos) if v.0.is_none() && v.3 == KEYWORD_THIS => { - return this_ptr.as_mut().map_or_else( - || Err(ERR::ErrorUnboundThis(*pos).into()), - |val| Ok(((*val).into(), *pos)), - ) + return if this_ptr.is_null() { + Err(ERR::ErrorUnboundThis(*pos).into()) + } else { + Ok((this_ptr.into(), *pos)) + }; } _ if global.always_search_scope => (0, expr.start_position()), Expr::Variable(.., Some(i), pos) => (i.get() as usize, *pos), @@ -224,7 +225,7 @@ impl Engine { lib: &[SharedModule], level: usize, scope: &mut Scope, - this_ptr: &mut Option<&mut Dynamic>, + this_ptr: &mut Dynamic, expr: &Expr, ) -> RhaiResult { // Coded this way for better branch prediction. @@ -256,10 +257,11 @@ impl Engine { self.track_operation(global, expr.position())?; return if index.is_none() && x.0.is_none() && x.3 == KEYWORD_THIS { - this_ptr - .as_deref() - .cloned() - .ok_or_else(|| ERR::ErrorUnboundThis(*var_pos).into()) + if this_ptr.is_null() { + ERR::ErrorUnboundThis(*var_pos).into() + } else { + Ok(this_ptr.clone()) + } } else { self.search_namespace(global, caches, lib, level, scope, this_ptr, expr) .map(|(val, ..)| val.take_or_clone()) diff --git a/src/eval/stmt.rs b/src/eval/stmt.rs index 1e232ddc..0f064711 100644 --- a/src/eval/stmt.rs +++ b/src/eval/stmt.rs @@ -29,7 +29,7 @@ impl Engine { lib: &[SharedModule], level: usize, scope: &mut Scope, - this_ptr: &mut Option<&mut Dynamic>, + this_ptr: &mut Dynamic, statements: &[Stmt], restore_orig_state: bool, ) -> RhaiResult { @@ -203,7 +203,7 @@ impl Engine { lib: &[SharedModule], level: usize, scope: &mut Scope, - this_ptr: &mut Option<&mut Dynamic>, + this_ptr: &mut Dynamic, stmt: &Stmt, rewind_scope: bool, ) -> RhaiResult { @@ -912,8 +912,10 @@ impl Engine { scope: &mut Scope, statements: &[Stmt], ) -> RhaiResult { + let mut this = Dynamic::NULL; + self.eval_stmt_block( - global, caches, lib, level, scope, &mut None, statements, false, + global, caches, lib, level, scope, &mut this, statements, false, ) .or_else(|err| match *err { ERR::Return(out, ..) => Ok(out), diff --git a/src/func/call.rs b/src/func/call.rs index d7f0102d..8bc4cfe3 100644 --- a/src/func/call.rs +++ b/src/func/call.rs @@ -11,8 +11,8 @@ use crate::eval::{Caches, FnResolutionCacheEntry, GlobalRuntimeState}; use crate::tokenizer::{is_valid_function_name, Token}; use crate::types::RestoreOnDrop; use crate::{ - calc_fn_hash, calc_fn_hash_full, Dynamic, Engine, FnArgsVec, FnPtr, ImmutableString, SharedModule, - OptimizationLevel, Position, RhaiError, RhaiResult, RhaiResultOf, Scope, ERR, + calc_fn_hash, calc_fn_hash_full, Dynamic, Engine, FnArgsVec, FnPtr, ImmutableString, + OptimizationLevel, Position, RhaiError, RhaiResult, RhaiResultOf, Scope, SharedModule, ERR, }; #[cfg(feature = "no_std")] use hashbrown::hash_map::Entry; @@ -407,6 +407,7 @@ impl Engine { }; if trigger { let scope = &mut &mut Scope::new(); + let mut this = Dynamic::NULL; let node = crate::ast::Stmt::Noop(pos); let node = (&node).into(); let event = match _result { @@ -415,7 +416,7 @@ impl Engine { }; if let Err(err) = self - .run_debugger_raw(global, caches, lib, level, scope, &mut None, node, event) + .run_debugger_raw(global, caches, lib, level, scope, &mut this, node, event) { _result = Err(err); } @@ -646,16 +647,7 @@ impl Engine { let (first_arg, rest_args) = args.split_first_mut().unwrap(); self.call_script_fn( - global, - caches, - lib, - level, - scope, - &mut Some(*first_arg), - func, - rest_args, - true, - pos, + global, caches, lib, level, scope, first_arg, func, rest_args, true, pos, ) } else { // Normal call of script function @@ -672,8 +664,10 @@ impl Engine { backup.restore_first_arg(a) }); + let mut this = Dynamic::NULL; + self.call_script_fn( - global, caches, lib, level, scope, &mut None, func, args, true, pos, + global, caches, lib, level, scope, &mut this, func, args, true, pos, ) } .map(|r| (r, false)); @@ -697,7 +691,7 @@ impl Engine { lib: &[SharedModule], level: usize, scope: &mut Scope, - this_ptr: &mut Option<&mut Dynamic>, + this_ptr: &mut Dynamic, arg_expr: &Expr, ) -> RhaiResultOf<(Dynamic, Position)> { // Literal values @@ -956,7 +950,7 @@ impl Engine { lib: &[SharedModule], level: usize, scope: &mut Scope, - this_ptr: &mut Option<&mut Dynamic>, + this_ptr: &mut Dynamic, fn_name: &str, op_token: Option<&Token>, first_arg: Option<&Expr>, @@ -1247,7 +1241,7 @@ impl Engine { lib: &[SharedModule], level: usize, scope: &mut Scope, - this_ptr: &mut Option<&mut Dynamic>, + this_ptr: &mut Dynamic, namespace: &crate::ast::Namespace, fn_name: &str, args_expr: &[Expr], @@ -1376,12 +1370,13 @@ impl Engine { Some(f) if f.is_script() => { let fn_def = f.get_script_fn_def().expect("script-defined function"); let new_scope = &mut Scope::new(); + let mut this = Dynamic::NULL; let orig_source = mem::replace(&mut global.source, module.id_raw().cloned()); let global = &mut *RestoreOnDrop::lock(global, move |g| g.source = orig_source); self.call_script_fn( - global, caches, lib, level, new_scope, &mut None, fn_def, &mut args, true, pos, + global, caches, lib, level, new_scope, &mut this, fn_def, &mut args, true, pos, ) } @@ -1474,7 +1469,7 @@ impl Engine { lib: &[SharedModule], level: usize, scope: &mut Scope, - this_ptr: &mut Option<&mut Dynamic>, + this_ptr: &mut Dynamic, expr: &FnCallExpr, pos: Position, ) -> RhaiResult { diff --git a/src/func/script.rs b/src/func/script.rs index f8553165..ae4a0f77 100644 --- a/src/func/script.rs +++ b/src/func/script.rs @@ -29,7 +29,7 @@ impl Engine { lib: &[SharedModule], level: usize, scope: &mut Scope, - this_ptr: &mut Option<&mut Dynamic>, + this_ptr: &mut Dynamic, fn_def: &ScriptFnDef, args: &mut FnCallArgs, rewind_scope: bool, diff --git a/src/optimizer.rs b/src/optimizer.rs index b9804024..7ad765c1 100644 --- a/src/optimizer.rs +++ b/src/optimizer.rs @@ -50,7 +50,7 @@ struct OptimizerState<'a> { /// Has the [`AST`] been changed during this pass? changed: bool, /// Collection of constants to use for eager function evaluations. - variables: StaticVec<(Identifier, AccessMode, Option)>, + variables: StaticVec<(Identifier, AccessMode, Dynamic)>, /// Activate constants propagation? propagate_constants: bool, /// An [`Engine`] instance for eager function evaluation. @@ -108,12 +108,7 @@ impl<'a> OptimizerState<'a> { } /// Add a new variable to the list. #[inline(always)] - pub fn push_var( - &mut self, - name: impl Into, - access: AccessMode, - value: Option, - ) { + pub fn push_var(&mut self, name: impl Into, access: AccessMode, value: Dynamic) { self.variables.push((name.into(), access, value)); } /// Look up a constant from the list. @@ -127,7 +122,8 @@ impl<'a> OptimizerState<'a> { if n == name { return match access { AccessMode::ReadWrite => None, - AccessMode::ReadOnly => value.as_ref(), + AccessMode::ReadOnly if value.is_null() => None, + AccessMode::ReadOnly => Some(value), }; } } @@ -141,7 +137,7 @@ impl<'a> OptimizerState<'a> { fn_name: &str, op_token: Option<&Token>, arg_values: &mut [Dynamic], - ) -> Option { + ) -> Dynamic { #[cfg(not(feature = "no_function"))] let lib = self.lib; #[cfg(feature = "no_function")] @@ -160,8 +156,7 @@ impl<'a> OptimizerState<'a> { false, Position::NONE, ) - .ok() - .map(|(v, ..)| v) + .map_or(Dynamic::NULL, |(v, ..)| v) } } @@ -271,13 +266,13 @@ fn optimize_stmt_block( state.push_var( x.0.as_str(), AccessMode::ReadOnly, - x.1.get_literal_value(), + x.1.get_literal_value().unwrap_or(Dynamic::NULL), ); } } else { // Add variables into the state optimize_expr(&mut x.1, state, false); - state.push_var(x.0.as_str(), AccessMode::ReadWrite, None); + state.push_var(x.0.as_str(), AccessMode::ReadWrite, Dynamic::NULL); } } // Optimize the statement @@ -1197,13 +1192,13 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, _chaining: bool) { let arg_values = &mut x.args.iter().map(Expr::get_literal_value).collect::>>().unwrap(); let result = match x.name.as_str() { - KEYWORD_TYPE_OF if arg_values.len() == 1 => Some(state.engine.map_type_name(arg_values[0].type_name()).into()), + KEYWORD_TYPE_OF if arg_values.len() == 1 => state.engine.map_type_name(arg_values[0].type_name()).into(), #[cfg(not(feature = "no_closure"))] - crate::engine::KEYWORD_IS_SHARED if arg_values.len() == 1 => Some(Dynamic::FALSE), + crate::engine::KEYWORD_IS_SHARED if arg_values.len() == 1 => Dynamic::FALSE, _ => state.call_fn_with_constant_arguments(&x.name, x.op_token.as_ref(), arg_values) }; - if let Some(result) = result { + if !result.is_null() { state.set_dirty(); *expr = Expr::from_dynamic(result, *pos); return; @@ -1289,15 +1284,15 @@ fn optimize_top_level( .rev() .flat_map(|m| m.iter_var()) { - state.push_var(name, AccessMode::ReadOnly, Some(value.clone())); + state.push_var(name, AccessMode::ReadOnly, value.clone()); } // Add constants and variables from the scope for (name, constant, value) in scope.iter() { if constant { - state.push_var(name, AccessMode::ReadOnly, Some(value)); + state.push_var(name, AccessMode::ReadOnly, value); } else { - state.push_var(name, AccessMode::ReadWrite, None); + state.push_var(name, AccessMode::ReadWrite, Dynamic::NULL); } } diff --git a/src/parser.rs b/src/parser.rs index fd315bad..03d5b16b 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -2907,7 +2907,7 @@ impl Engine { will_shadow, }; let caches = &mut Caches::new(); - let this_ptr = &mut None; + let mut this = Dynamic::NULL; let context = EvalContext::new( self, @@ -2916,7 +2916,7 @@ impl Engine { &[], level, &mut state.stack, - this_ptr, + &mut this, ); match filter(false, info, context) { diff --git a/src/serde/de.rs b/src/serde/de.rs index fcb26a9d..42d4739f 100644 --- a/src/serde/de.rs +++ b/src/serde/de.rs @@ -125,6 +125,8 @@ impl<'de> Deserializer<'de> for DynamicDeserializer<'de> { fn deserialize_any>(self, visitor: V) -> RhaiResultOf { match self.0 .0 { + Union::Null => unreachable!(), + Union::Unit(..) => self.deserialize_unit(visitor), Union::Bool(..) => self.deserialize_bool(visitor), Union::Str(..) => self.deserialize_str(visitor), diff --git a/src/serde/serialize.rs b/src/serde/serialize.rs index 424de0d5..0e9c481f 100644 --- a/src/serde/serialize.rs +++ b/src/serde/serialize.rs @@ -15,6 +15,8 @@ use crate::types::dynamic::Variant; impl Serialize for Dynamic { fn serialize(&self, ser: S) -> Result { match self.0 { + Union::Null => unreachable!(), + Union::Unit(..) => ser.serialize_unit(), Union::Bool(x, ..) => ser.serialize_bool(x), Union::Str(ref s, ..) => ser.serialize_str(s.as_str()), diff --git a/src/types/dynamic.rs b/src/types/dynamic.rs index c53b8ea3..eeee966a 100644 --- a/src/types/dynamic.rs +++ b/src/types/dynamic.rs @@ -55,6 +55,9 @@ pub struct Dynamic(pub(crate) Union); /// /// Most variants are boxed to reduce the size. pub enum Union { + /// An error value which should not exist. + Null, + /// The Unit value - (). Unit((), Tag, AccessMode), /// A boolean value. @@ -178,6 +181,8 @@ impl Dynamic { #[must_use] pub const fn tag(&self) -> Tag { match self.0 { + Union::Null => unreachable!(), + Union::Unit(_, tag, _) | Union::Bool(_, tag, _) | Union::Str(_, tag, _) @@ -203,6 +208,8 @@ impl Dynamic { /// Attach arbitrary data to this [`Dynamic`]. pub fn set_tag(&mut self, value: Tag) -> &mut Self { match self.0 { + Union::Null => unreachable!(), + Union::Unit(_, ref mut tag, _) | Union::Bool(_, ref mut tag, _) | Union::Str(_, ref mut tag, _) @@ -226,6 +233,12 @@ impl Dynamic { } self } + /// Is this [`Dynamic`] null? + #[inline(always)] + #[must_use] + pub(crate) const fn is_null(&self) -> bool { + matches!(self.0, Union::Null) + } /// Does this [`Dynamic`] hold a variant data type instead of one of the supported system /// primitive types? #[inline(always)] @@ -307,6 +320,8 @@ impl Dynamic { #[must_use] pub fn type_id(&self) -> TypeId { match self.0 { + Union::Null => unreachable!(), + Union::Unit(..) => TypeId::of::<()>(), Union::Bool(..) => TypeId::of::(), Union::Str(..) => TypeId::of::(), @@ -341,6 +356,8 @@ impl Dynamic { #[must_use] pub fn type_name(&self) -> &'static str { match self.0 { + Union::Null => unreachable!(), + Union::Unit(..) => "()", Union::Bool(..) => "bool", Union::Str(..) => "string", @@ -385,6 +402,8 @@ impl Hash for Dynamic { mem::discriminant(&self.0).hash(state); match self.0 { + Union::Null => unreachable!(), + Union::Unit(..) => (), Union::Bool(ref b, ..) => b.hash(state), Union::Str(ref s, ..) => s.hash(state), @@ -416,6 +435,8 @@ impl Hash for Dynamic { impl fmt::Display for Dynamic { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self.0 { + Union::Null => unreachable!(), + Union::Unit(..) => Ok(()), Union::Bool(ref v, ..) => fmt::Display::fmt(v, f), Union::Str(ref v, ..) => fmt::Display::fmt(v, f), @@ -509,6 +530,8 @@ impl fmt::Debug for Dynamic { #[inline(never)] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self.0 { + Union::Null => unreachable!(), + Union::Unit(ref v, ..) => fmt::Debug::fmt(v, f), Union::Bool(ref v, ..) => fmt::Debug::fmt(v, f), Union::Str(ref v, ..) => fmt::Debug::fmt(v, f), @@ -619,6 +642,8 @@ impl Clone for Dynamic { /// The cloned copy is marked read-write even if the original is read-only. fn clone(&self) -> Self { match self.0 { + Union::Null => unreachable!(), + Union::Unit(v, tag, ..) => Self(Union::Unit(v, tag, ReadWrite)), Union::Bool(v, tag, ..) => Self(Union::Bool(v, tag, ReadWrite)), Union::Str(ref v, tag, ..) => Self(Union::Str(v.clone(), tag, ReadWrite)), @@ -666,6 +691,9 @@ use std::f32::consts as FloatConstants; use std::f64::consts as FloatConstants; impl Dynamic { + /// A [`Dynamic`] containing a `null`. + pub(crate) const NULL: Self = Self(Union::Null); + /// A [`Dynamic`] containing a `()`. pub const UNIT: Self = Self(Union::Unit((), DEFAULT_TAG_VALUE, ReadWrite)); /// A [`Dynamic`] containing a `true`. @@ -888,6 +916,8 @@ impl Dynamic { #[must_use] pub(crate) const fn access_mode(&self) -> AccessMode { match self.0 { + Union::Null => unreachable!(), + Union::Unit(.., access) | Union::Bool(.., access) | Union::Str(.., access) @@ -913,6 +943,8 @@ impl Dynamic { /// Set the [`AccessMode`] for this [`Dynamic`]. pub(crate) fn set_access_mode(&mut self, typ: AccessMode) -> &mut Self { match self.0 { + Union::Null => unreachable!(), + Union::Unit(.., ref mut access) | Union::Bool(.., ref mut access) | Union::Str(.., ref mut access) @@ -1107,6 +1139,7 @@ impl Dynamic { let _access = self.access_mode(); match self.0 { + Union::Null => unreachable!(), Union::Shared(..) => self, _ => Self(Union::Shared( crate::Locked::new(self).into(), @@ -1151,6 +1184,8 @@ impl Dynamic { reify!(self, |v: T| return Some(v)); match self.0 { + Union::Null => unreachable!(), + Union::Int(v, ..) => reify!(v => Option), #[cfg(not(feature = "no_float"))] Union::Float(v, ..) => reify!(*v => Option), @@ -1485,6 +1520,7 @@ impl Dynamic { } match self.0 { + Union::Null => unreachable!(), Union::Variant(ref v, ..) => (***v).as_any().downcast_ref::(), #[cfg(not(feature = "no_closure"))] Union::Shared(..) => None, @@ -1583,6 +1619,7 @@ impl Dynamic { } match self.0 { + Union::Null => unreachable!(), Union::Variant(ref mut v, ..) => (***v).as_any_mut().downcast_mut::(), #[cfg(not(feature = "no_closure"))] Union::Shared(..) => None, diff --git a/unreachable!() b/unreachable!() new file mode 100644 index 00000000..e69de29b