[gccrs COMMIT 1/2] lang: Add DST support to box and improve box dispatch

gerris.rs@gmail.com gerris.rs@gmail.com
Sun Aug 16 01:07:51 GMT 2026


From: Enes Cevik <enes@nsvke.com>

This patch introduces DST support for box, allowing it to correctly
handle unsized types.

It also implements the auto-deref mechanism for box. This ensures that
method dispatch, indexing, and tuple field access work correctly.

gcc/rust/ChangeLog:

	* backend/rust-compile-expr.cc (compile_box): Remove DST guard.
	(build_box_inner_ptr): Add fat pointer control.
	(CompileExpr::visit): Add auto-deref for box.
	(HIRCompileBase::resolve_deref_adjustment): Return fat or thin
	pointer.
	* typecheck/rust-hir-type-check-expr.cc (TypeCheckExpr::visit):
	Add auto-deref for box.
	* typecheck/rust-tyty.cc (ADTType::is_box): New function.
	* typecheck/rust-tyty.h (class BaseType): New declaration.

gcc/testsuite/ChangeLog:

	* rust/execute/box-dispatch-1.rs: New test.
	* rust/execute/box-dispatch-2.rs: New test.
	* rust/execute/unsized-adt-size.rs: Format test.

Signed-off-by: Enes Cevik <enes@nsvke.com>
---
This change was merged into the gccrs repository and is posted here for
upstream visibility and potential drive-by review, as requested by GCC
release managers.
Each commit email contains a link to its details on github from where you can
find the Pull-Request and associated discussions.


Commit on github: https://github.com/Rust-GCC/gccrs/commit/c8230e04f08742c916e9248bc4357b4e4b686581

The commit has NOT been mentioned in any issue.

The commit has been mentioned in the following pull-request(s):
 - https://github.com/Rust-GCC/gccrs/pull/4767

 gcc/rust/backend/rust-compile-expr.cc         | 62 +++++++++------
 .../typecheck/rust-hir-type-check-expr.cc     | 11 +++
 gcc/rust/typecheck/rust-tyty.cc               |  9 +++
 gcc/rust/typecheck/rust-tyty.h                |  3 +
 gcc/testsuite/rust/execute/box-dispatch-1.rs  | 47 ++++++++++++
 gcc/testsuite/rust/execute/box-dispatch-2.rs  | 76 +++++++++++++++++++
 .../rust/execute/unsized-adt-size.rs          | 13 ++--
 7 files changed, 190 insertions(+), 31 deletions(-)
 create mode 100644 gcc/testsuite/rust/execute/box-dispatch-1.rs
 create mode 100644 gcc/testsuite/rust/execute/box-dispatch-2.rs

diff --git a/gcc/rust/backend/rust-compile-expr.cc b/gcc/rust/backend/rust-compile-expr.cc
index 12369f583..be092070b 100644
--- a/gcc/rust/backend/rust-compile-expr.cc
+++ b/gcc/rust/backend/rust-compile-expr.cc
@@ -101,12 +101,6 @@ compile_box (Context *ctx, TyTy::BaseType *box_tyty, TyTy::BaseType *inner_tyty,
 {
   tree inner_type_tree = TyTyResolveCompile::compile (ctx, inner_tyty);
 
-  if (!COMPLETE_TYPE_P (inner_type_tree))
-    {
-      rust_sorry_at (locus,
-		     "dynamically sized types in boxes are not supported yet");
-      return error_mark_node;
-    }
   tree size_tree = TYPE_SIZE_UNIT (inner_type_tree);
   tree align_tree
     = build_int_cst (size_type_node, TYPE_ALIGN_UNIT (inner_type_tree));
@@ -145,6 +139,9 @@ build_box_inner_ptr (tree main_expr, location_t locus)
   // custom allocator is placed as the first field in the RECORD_TYPE.
   while (TREE_CODE (TREE_TYPE (main_expr)) == RECORD_TYPE)
     {
+      if (RS_DST_FLAG_P (TREE_TYPE (main_expr)))
+	break;
+
       tree first_field = TYPE_FIELDS (TREE_TYPE (main_expr));
       if (first_field == NULL_TREE)
 	break;
@@ -187,6 +184,13 @@ CompileExpr::visit (HIR::TupleIndexExpr &expr)
       tree indirect = indirect_expression (receiver_ref, expr.get_locus ());
       receiver_ref = indirect;
     }
+  else if (auto inner_expr_ty = TyTy::try_get_box_inner_type (tuple_expr_ty))
+    {
+      rust_assert (inner_expr_ty.value ()->get_kind ()
+		   == TyTy::TypeKind::TUPLE);
+      receiver_ref = build_box_inner_ptr (receiver_ref, expr.get_locus ());
+      receiver_ref = indirect_expression (receiver_ref, expr.get_locus ());
+    }
 
   translated
     = Backend::struct_field_expression (receiver_ref, index, expr.get_locus ());
@@ -1781,6 +1785,21 @@ CompileExpr::visit (HIR::MethodCallExpr &expr)
     expr.get_receiver ().get_mappings ().get_hirid (), &receiver);
   rust_assert (ok);
 
+  // lookup the autoderef mappings
+  HirId autoderef_mappings_id
+    = expr.get_receiver ().get_mappings ().get_hirid ();
+  std::vector<Resolver::Adjustment> *adjustments = nullptr;
+  ok = ctx->get_tyctx ()->lookup_autoderef_mappings (autoderef_mappings_id,
+						     &adjustments);
+  rust_assert (ok);
+
+  // apply adjustments for the fn call
+  self = resolve_adjustments (*adjustments, self,
+			      expr.get_receiver ().get_locus ());
+
+  if (adjustments != nullptr && !adjustments->empty ())
+    receiver = adjustments->back ().get_expected ();
+
   bool is_dyn_dispatch
     = receiver->get_root ()->get_kind () == TyTy::TypeKind::DYNAMIC;
   bool is_generic_receiver = receiver->get_kind () == TyTy::TypeKind::PARAM;
@@ -1804,18 +1823,6 @@ CompileExpr::visit (HIR::MethodCallExpr &expr)
     // lookup compiled functions since it may have already been compiled
     fn_expr = resolve_method_address (fntype, receiver, expr.get_locus ());
 
-  // lookup the autoderef mappings
-  HirId autoderef_mappings_id
-    = expr.get_receiver ().get_mappings ().get_hirid ();
-  std::vector<Resolver::Adjustment> *adjustments = nullptr;
-  ok = ctx->get_tyctx ()->lookup_autoderef_mappings (autoderef_mappings_id,
-						     &adjustments);
-  rust_assert (ok);
-
-  // apply adjustments for the fn call
-  self = resolve_adjustments (*adjustments, self,
-			      expr.get_receiver ().get_locus ());
-
   std::vector<tree> args;
   args.push_back (self); // adjusted self
 
@@ -2501,12 +2508,14 @@ HIRCompileBase::resolve_deref_adjustment (Resolver::Adjustment &adjustment,
 	       || adjustment.is_deref_mut_adjustment ());
   if (!adjustment.has_operator_overload ())
     {
-      TyTy::BaseType *receiver = adjustment.get_actual ();
-      if (TyTy::try_get_box_inner_type (receiver))
+      if (TyTy::try_get_box_inner_type (adjustment.get_actual ()))
 	{
-	  tree receiver_ref = expression;
-	  receiver_ref = build_box_inner_ptr (receiver_ref, locus);
-	  return indirect_expression (receiver_ref, locus);
+	  expression = build_box_inner_ptr (expression, locus);
+	  if (TREE_CODE (TREE_TYPE (expression)) == POINTER_TYPE
+	      || TREE_CODE (TREE_TYPE (expression)) == REFERENCE_TYPE)
+	    expression = indirect_expression (expression, locus);
+	  return expression;
+	  ;
 	}
       rust_assert (false);
     }
@@ -2931,6 +2940,13 @@ CompileExpr::visit (HIR::ArrayIndexExpr &expr)
       array_reference
 	= indirect_expression (array_reference, expr.get_locus ());
     }
+  else if (TyTy::try_get_box_inner_type (array_expr_ty))
+    {
+      array_reference
+	= build_box_inner_ptr (array_reference, expr.get_locus ());
+      array_reference
+	= indirect_expression (array_reference, expr.get_locus ());
+    }
 
   translated = Backend::array_index_expression (array_reference, index,
 						expr.get_locus ());
diff --git a/gcc/rust/typecheck/rust-hir-type-check-expr.cc b/gcc/rust/typecheck/rust-hir-type-check-expr.cc
index 41d71ccfd..fd9d1f953 100644
--- a/gcc/rust/typecheck/rust-hir-type-check-expr.cc
+++ b/gcc/rust/typecheck/rust-hir-type-check-expr.cc
@@ -95,6 +95,12 @@ TypeCheckExpr::visit (HIR::TupleIndexExpr &expr)
       return;
     }
 
+  // Box<T> autoderef
+  if (auto resolved_base = TyTy::try_get_box_inner_type (resolved))
+    {
+      resolved = *resolved_base;
+    }
+
   // FIXME does this require autoderef here?
   if (resolved->get_kind () == TyTy::TypeKind::REF)
     {
@@ -1087,6 +1093,11 @@ TypeCheckExpr::visit (HIR::ArrayIndexExpr &expr)
       if (base->get_kind () == TyTy::TypeKind::ARRAY)
 	direct_array_expr_ty = base;
     }
+  // Box<T> autoderef
+  else if (auto base = TyTy::try_get_box_inner_type (direct_array_expr_ty))
+    {
+      direct_array_expr_ty = *base;
+    }
 
   TyTy::BaseType *size_ty;
   bool ok = context->lookup_builtin ("usize", &size_ty);
diff --git a/gcc/rust/typecheck/rust-tyty.cc b/gcc/rust/typecheck/rust-tyty.cc
index f77d796d1..ad487208c 100644
--- a/gcc/rust/typecheck/rust-tyty.cc
+++ b/gcc/rust/typecheck/rust-tyty.cc
@@ -2186,6 +2186,15 @@ ADTType::is_unsized () const
   return last_field_type->is_unsized ();
 }
 
+bool
+ADTType::is_box () const
+{
+  if (auto owned_box = mappings.lookup_lang_item (LangItem::Kind::OWNED_BOX))
+    if (get_id () == owned_box)
+      return true;
+  return false;
+}
+
 // TupleType
 
 TupleType::TupleType (HirId ref, location_t locus, std::vector<TyVar> fields,
diff --git a/gcc/rust/typecheck/rust-tyty.h b/gcc/rust/typecheck/rust-tyty.h
index c51bb359a..3d910212a 100644
--- a/gcc/rust/typecheck/rust-tyty.h
+++ b/gcc/rust/typecheck/rust-tyty.h
@@ -363,6 +363,8 @@ public:
   // is_unsized returns true if the type is a DST
   virtual bool is_unsized () const { return false; }
 
+  virtual bool is_box () const { return false; }
+
 protected:
   BaseType (HirId ref, HirId ty_ref, TypeKind kind, RustIdent ident,
 	    std::set<HirId> refs = std::set<HirId> ());
@@ -1046,6 +1048,7 @@ public:
 
   bool contains_unsafe_cell () const override;
   virtual bool is_unsized () const override;
+  virtual bool is_box () const override;
 
 private:
   DefId id;
diff --git a/gcc/testsuite/rust/execute/box-dispatch-1.rs b/gcc/testsuite/rust/execute/box-dispatch-1.rs
new file mode 100644
index 000000000..50e05ec43
--- /dev/null
+++ b/gcc/testsuite/rust/execute/box-dispatch-1.rs
@@ -0,0 +1,47 @@
+#![feature(no_core, lang_items, box_syntax)]
+#![no_core]
+
+extern "C" {
+    fn malloc(size: usize) -> *mut u8;
+}
+
+#[lang = "sized"]
+pub trait Sized {}
+
+#[lang = "owned_box"]
+pub struct Box<T: ?Sized>(*mut T);
+
+#[lang = "exchange_malloc"]
+pub unsafe fn exchange_malloc(size: usize, _align: usize) -> *mut u8 {
+    malloc(size)
+}
+
+impl<T> Box<T> {
+    pub fn new(x: T) -> Box<T> {
+        box x
+    }
+}
+
+pub trait Animal {
+    fn speak(&self) -> i32;
+}
+
+pub struct Dog {
+    pub code: i32,
+}
+
+impl Animal for Dog {
+    fn speak(&self) -> i32 {
+        self.code
+    }
+}
+
+fn main() -> i32 {
+    let dog1 = Dog { code: 111 };
+
+    let animal_ref: &dyn Animal = &dog1;
+
+    let box_of_ref: Box<&dyn Animal> = Box::new(animal_ref);
+
+    box_of_ref.speak() - 111
+}
diff --git a/gcc/testsuite/rust/execute/box-dispatch-2.rs b/gcc/testsuite/rust/execute/box-dispatch-2.rs
new file mode 100644
index 000000000..0d962778d
--- /dev/null
+++ b/gcc/testsuite/rust/execute/box-dispatch-2.rs
@@ -0,0 +1,76 @@
+#![feature(no_core, lang_items, box_syntax, intrinsics)]
+#![no_core]
+
+extern "C" {
+    fn malloc(size: usize) -> *mut u8;
+}
+
+extern "rust-intrinsic" {
+    fn offset<T>(dst: *const T, offset: isize) -> *const T;
+}
+
+#[lang = "index"]
+pub trait Index<Idx> {
+    type Output: ?Sized;
+    fn index(&self, index: Idx) -> &Self::Output;
+}
+
+impl Index<usize> for [i32] {
+    type Output = i32;
+
+    fn index(&self, index: usize) -> &i32 {
+        unsafe {
+            let ptr = self as *const [i32] as *const i32;
+            &*offset(ptr, index as isize)
+        }
+    }
+}
+
+#[lang = "sized"]
+pub trait Sized {}
+
+#[lang = "owned_box"]
+pub struct Box<T: ?Sized>(*mut T);
+
+#[lang = "exchange_malloc"]
+pub unsafe fn em(size: usize, _align: usize) -> *mut u8 {
+    malloc(size)
+}
+
+impl<T> Box<T> {
+    pub fn new(x: T) -> Box<T> {
+        box x
+    }
+}
+
+pub struct X { data: i32 }
+
+pub trait A {
+    fn a(&self) -> i32;
+}
+
+impl A for X {
+    fn a(&self) -> i32 {
+        self.data
+    }
+}
+pub fn main() -> i32 {
+    let x = X { data: 44 };
+    let y = X { data: 22 };
+    let z : [i32; 3] = [1, 2, 3];
+    let w : (i32, i32) = (10, 20);
+
+    let a : Box<dyn A> = Box::new(x);
+    let b : Box<X> = Box::new(y);
+    let c : Box<[i32; 3]> = Box::new(z);
+    let d : Box<(i32, i32)> = Box::new(w);
+    let e : Box<[i32]> = Box::new(z);
+
+    a.a() - 2 * b.data // 44 - 2 * 22
+    +
+    c[0] + c[1] - c[2] // 1 + 2 - 3
+    +
+    2 * d.0 - d.1 // 2 * 10 - 20
+    +
+    e[0] + e[1] - e[2] // 1 + 2 - 3
+}
diff --git a/gcc/testsuite/rust/execute/unsized-adt-size.rs b/gcc/testsuite/rust/execute/unsized-adt-size.rs
index c3210a941..aa2153c37 100644
--- a/gcc/testsuite/rust/execute/unsized-adt-size.rs
+++ b/gcc/testsuite/rust/execute/unsized-adt-size.rs
@@ -59,15 +59,12 @@ pub fn sov3(s: &TailStruct<TailStruct<[i32]>>) -> usize {
 fn main() -> i32 {
     let t = [1, 2, 3];
     let t1 = &t as &[i32];
-    let s1 : TailStruct<[i32; 3]> = TailStruct {
-        a: 10,
-        tail: t,
-    };
-    let s2_tail: TailStruct<[i32; 3]> = TailStruct {
-        a: 10,
-        tail: t,
+    let s1: TailStruct<[i32; 3]> = TailStruct { a: 10, tail: t };
+    let s2_tail: TailStruct<[i32; 3]> = TailStruct { a: 10, tail: t };
+    let s2: TailStruct<TailStruct<[i32; 3]>> = TailStruct {
+        a: 20,
+        tail: s2_tail,
     };
-    let s2 : TailStruct<TailStruct<[i32; 3]>> = TailStruct { a: 20, tail: s2_tail };
 
     let a = sov1(t1);
     let b = sov2(&s1);

base-commit: adf05734d896265d69cf64ec53bf8147556e1a77
-- 
2.54.0



More information about the Gcc-rust mailing list