This is an automated email from the ASF dual-hosted git repository.

chaokunyang pushed a commit to tag v0.13.2-rc1
in repository https://gitbox.apache.org/repos/asf/fory.git

commit 103e4bd7229ffcf0ff8961ca1ceaa7c697169a24
Author: Shawn Yang <[email protected]>
AuthorDate: Thu Nov 27 12:00:39 2025 +0800

    fix(rust): raise error for nested polymorphics for collection (#2934)
    
    ## Why?
    
    <!-- Describe the purpose of this PR. -->
    
    ## What does this PR do?
    
    <!-- Describe the details of this PR. -->
    
    ## Related issues
    
    Closes #2932
    
    ## Does this PR introduce any user-facing change?
    
    <!--
    If any user-facing interface changes, please [open an
    issue](https://github.com/apache/fory/issues/new/choose) describing the
    need to do so and update the document if necessary.
    
    Delete section if not applicable.
    -->
    
    - [ ] Does this PR introduce any public API change?
    - [ ] Does this PR introduce any binary protocol compatibility change?
    
    ## Benchmark
    
    <!--
    When the PR has an impact on performance (if you don't know whether the
    PR will have an impact on performance, you can submit the PR first, and
    if it will have impact on performance, the code reviewer will explain
    it), be sure to attach a benchmark data here.
    
    Delete section if not applicable.
    -->
---
 rust/fory-core/src/serializer/any.rs | 31 ++++++++++++++++++++++++
 rust/tests/tests/test_any.rs         | 46 ++++++++++++++++++++++++++++++++++++
 2 files changed, 77 insertions(+)

diff --git a/rust/fory-core/src/serializer/any.rs 
b/rust/fory-core/src/serializer/any.rs
index b2e0b6d71..f90737a87 100644
--- a/rust/fory-core/src/serializer/any.rs
+++ b/rust/fory-core/src/serializer/any.rs
@@ -23,10 +23,29 @@ use crate::serializer::util::write_dyn_data_generic;
 use crate::serializer::{ForyDefault, Serializer};
 use crate::types::RefFlag;
 use crate::types::TypeId;
+use crate::types::{LIST, MAP, SET};
 use std::any::Any;
 use std::rc::Rc;
 use std::sync::Arc;
 
+/// Check if the type info represents a generic container type (LIST, SET, 
MAP).
+/// These types cannot be deserialized polymorphically via Box<dyn Any> because
+/// different generic instantiations (e.g., Vec<A>, Vec<B>) share the same 
type ID.
+#[inline]
+fn check_generic_container_type(type_info: &TypeInfo) -> Result<(), Error> {
+    let type_id = type_info.get_type_id();
+    if type_id == LIST || type_id == SET || type_id == MAP {
+        return Err(Error::type_error(
+            "Cannot deserialize generic container types (Vec, HashSet, 
HashMap) polymorphically \
+            via Box/Rc/Arc/Weak<dyn Any>. The serialization protocol does not 
preserve the element type \
+            information needed to distinguish between different generic 
instantiations \
+            (e.g., Vec<StructA> vs Vec<StructB>). Consider wrapping the 
container in a \
+            named struct type instead.",
+        ));
+    }
+    Ok(())
+}
+
 /// Helper function to deserialize to `Box<dyn Any>`
 pub fn deserialize_any_box(context: &mut ReadContext) -> Result<Box<dyn Any>, 
Error> {
     context.inc_depth()?;
@@ -35,6 +54,8 @@ pub fn deserialize_any_box(context: &mut ReadContext) -> 
Result<Box<dyn Any>, Er
         return Err(Error::invalid_ref("Expected NotNullValue for Box<dyn 
Any>"));
     }
     let typeinfo = context.read_any_typeinfo()?;
+    // Check for generic container types which cannot be deserialized 
polymorphically
+    check_generic_container_type(&typeinfo)?;
     let deserializer_fn = typeinfo.get_harness().get_read_data_fn();
     let result = deserializer_fn(context);
     context.dec_depth();
@@ -194,6 +215,8 @@ pub fn read_box_any(
         );
         context.read_any_typeinfo()?
     };
+    // Check for generic container types which cannot be deserialized 
polymorphically
+    check_generic_container_type(&typeinfo)?;
     let deserializer_fn = typeinfo.get_harness().get_read_data_fn();
     let result = deserializer_fn(context);
     context.dec_depth();
@@ -346,6 +369,8 @@ pub fn read_rc_any(
             } else {
                 type_info.ok_or_else(|| Error::type_error("No type info found 
for read"))?
             };
+            // Check for generic container types which cannot be deserialized 
polymorphically
+            check_generic_container_type(&typeinfo)?;
             let read_data_fn = typeinfo.get_harness().get_read_data_fn();
             let boxed = read_data_fn(context)?;
             context.dec_depth();
@@ -358,6 +383,8 @@ pub fn read_rc_any(
             } else {
                 type_info.ok_or_else(|| Error::type_error("No type info found 
for read"))?
             };
+            // Check for generic container types which cannot be deserialized 
polymorphically
+            check_generic_container_type(&typeinfo)?;
             let read_data_fn = typeinfo.get_harness().get_read_data_fn();
             let boxed = read_data_fn(context)?;
             context.dec_depth();
@@ -516,6 +543,8 @@ pub fn read_arc_any(
                 type_info
                     .ok_or_else(|| Error::type_error("No type info found for 
read Arc<dyn Any>"))?
             };
+            // Check for generic container types which cannot be deserialized 
polymorphically
+            check_generic_container_type(&typeinfo)?;
             let read_data_fn = typeinfo.get_harness().get_read_data_fn();
             let boxed = read_data_fn(context)?;
             context.dec_depth();
@@ -529,6 +558,8 @@ pub fn read_arc_any(
                 type_info
                     .ok_or_else(|| Error::type_error("No type info found for 
read Arc<dyn Any>"))?
             };
+            // Check for generic container types which cannot be deserialized 
polymorphically
+            check_generic_container_type(&typeinfo)?;
             let read_data_fn = typeinfo.get_harness().get_read_data_fn();
             let boxed = read_data_fn(context)?;
             context.dec_depth();
diff --git a/rust/tests/tests/test_any.rs b/rust/tests/tests/test_any.rs
index 13929f5cc..3bdb804cb 100644
--- a/rust/tests/tests/test_any.rs
+++ b/rust/tests/tests/test_any.rs
@@ -268,3 +268,49 @@ fn test_rc_by_name() {
         &deserialized_vec[1]
     ));
 }
+
+// Tests for GitHub issue: multiple Vec<Struct> types wrapped in Box<dyn Any>
+// This reproduces the non-deterministic deserialization failures
+#[derive(ForyObject, Debug, Clone, PartialEq)]
+struct StructA {
+    a: i32,
+}
+
+#[derive(ForyObject, Debug, Clone, PartialEq)]
+struct StructB {
+    i: i32,
+}
+
+/// Test that serializing different Vec<T> types in Box<dyn Any> returns a 
clear error.
+///
+/// This tests for a known limitation of the xlang serialization protocol:
+/// different generic container types (Vec<StructA>, Vec<StructB>) share the 
same
+/// type ID (LIST=21), so they cannot be distinguished during polymorphic 
deserialization.
+///
+/// Previously this caused non-deterministic failures. Now it returns a clear 
error message.
+#[test]
+fn test_vec_of_different_struct_types_in_box_any_returns_error() {
+    let mut fory = Fory::default();
+    // Register both struct types
+    fory.register_by_name::<StructA>("StructA").unwrap();
+    fory.register_generic_trait::<Vec<StructA>>().unwrap();
+    fory.register_by_name::<StructB>("StructB").unwrap();
+    fory.register_generic_trait::<Vec<StructB>>().unwrap();
+
+    // Create Box<dyn Any> wrappers for Vec<StructA> and Vec<StructB>
+    let a_vec: Box<dyn Any> = Box::new(vec![StructA { a: 11 }; 5]);
+    let b_vec: Box<dyn Any> = Box::new(vec![StructB { i: 1 }; 5]);
+    let any_vec: Vec<Box<dyn Any>> = vec![a_vec, b_vec];
+
+    let bytes = fory.serialize(&any_vec).unwrap();
+
+    // Deserialization should fail with a clear error about generic containers
+    let result: Result<Vec<Box<dyn Any>>, _> = fory.deserialize(&bytes);
+    assert!(result.is_err());
+    let err_msg = result.unwrap_err().to_string();
+    assert!(
+        err_msg.contains("generic container types"),
+        "Expected error about generic containers, got: {}",
+        err_msg
+    );
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to