chiark / gitweb /
xdata: fix downcasting to actually work
authorIan Jackson <ijackson@chiark.greenend.org.uk>
Sun, 14 Feb 2021 16:02:35 +0000 (16:02 +0000)
committerIan Jackson <ijackson@chiark.greenend.org.uk>
Sun, 14 Feb 2021 16:03:30 +0000 (16:03 +0000)
Urgh, this involved a lot of flail before I found (i) downcast-rs
(ii) the conditional lifetime bug.

Signed-off-by: Ian Jackson <ijackson@chiark.greenend.org.uk>
src/gamestate.rs
src/imports.rs

index 00cb748c1ef4afc85c25975e624f309918d67b4a..d7a872c021c261c13eee7e12a7116dbb3ea0faa5 100644 (file)
@@ -101,11 +101,14 @@ pub struct CommittedLogEntry {
 // ---------- piece trait, and rendering ----------
 
 #[typetag::serde(tag="type")]
-pub trait PieceXData: Any + Debug + Send + 'static {
+pub trait PieceXData: Downcast + Debug + Send + 'static {
   fn default() -> Box<dyn PieceXData> where Self: Default {
-    Box::new(<Self as Default>::default())
+    let k = Box::new(<Self as Default>::default());
+    dbg!(&k);
+    k
   }
 }
+impl_downcast!(PieceXData);
 
 #[typetag::serde]
 pub trait Outline: Send + Debug {
@@ -286,22 +289,43 @@ impl PieceState {
 }
 
 pub trait PieceXDataExt {
-  fn get<T:PieceXData>(&self) -> Result<Option<&T>, IE>;
+  fn get<T:PieceXData+Default>(&self) -> Result<Option<&T>, IE>;
   fn get_mut<T:PieceXData+Default>(&mut self) -> Result<&mut T, IE>;
 }
+
+fn xdata_unexpected<T:PieceXData+Default>(got: &dyn PieceXData)
+                                          -> InternalError {
+  internal_logic_error(format!(
+    "\n\
+     piece xdata unexpectedly: {:?}\n\
+     expected something like:  Some({:?})\n",
+    &got, <T as PieceXData>::default(),
+  ))
+}
+
 impl PieceXDataExt for PieceXDataState {
   #[throws(IE)]
-  fn get<T:PieceXData>(&self) -> Option<&T> {
-    let m = format!("piece xdata unexpectedly {:?}", &self);
+  fn get<T:PieceXData>(&self) -> Option<&T> where T: Default {
     let xdata = if let Some(xdata) = &self { xdata } else { return None };
-    Some(Any::downcast_ref(xdata).ok_or_else(|| internal_logic_error(m))?)
+    let xdata: &dyn PieceXData = xdata.as_ref();
+    if let Some(y) = xdata.downcast_ref::<T>() { Some(y) }
+    else { throw!(xdata_unexpected::<T>(xdata)) }
   }
 
-  #[throws(IE)]
-  fn get_mut<T:PieceXData+Default>(&mut self) -> &mut T {
-    let m = format!("piece xdata unexpectedly {:?}", &self);
+  fn get_mut<T:PieceXData+Default>(&mut self) -> Result<&mut T, IE> {
     let xdata = self.get_or_insert_with(|| <T as PieceXData>::default());
-    Any::downcast_mut(xdata).ok_or_else(|| internal_logic_error(m))?
+    let xdata: &mut dyn PieceXData = xdata.as_mut();
+    let keep: *mut dyn PieceXData = xdata;
+    if let Some(y) = xdata.downcast_mut::<T>() { return Ok(y) }
+    
+    // Erroneous borrowck error with early returns
+    // https://github.com/rust-lang/rust/issues/58910
+    // https://github.com/rust-lang/rust/issues/51545
+    // Safety: the `xdata` borrow that was passed to `downcast_mut`
+    // is no longer present in this branch, so now we have only `keep`
+    // and the things `keep` was borrowed from.
+    let xdata: &dyn PieceXData = unsafe { keep.as_ref().unwrap() };
+    Err(xdata_unexpected::<T>(xdata))
   }
 }
 
index 2146afa3875e75cd7b92312aaca3a55fb27f4c03..2154cd74864e21bb709fd54ad7139d81b3097df3 100644 (file)
@@ -48,6 +48,7 @@ pub use arrayvec::ArrayVec;
 pub use boolinator::Boolinator as _;
 pub use delegate::delegate;
 pub use derive_more::*;
+pub use downcast_rs::{impl_downcast, Downcast};
 pub use either::{Either, Left, Right};
 pub use enum_map::{Enum, EnumMap};
 pub use fehler::{throw, throws};