[gccrs COMMIT 3/6] typecheck: Add ADT to ADT unsize coercion support

[email protected]
Newsgroups gmane.comp.gcc.rust,gmane.comp.gcc.patches
Message-ID <[email protected]>
From: Enes Cevik <[email protected]>

This patch adds basic support for ADT to ADT unsized coercions. It also
refactors the main coerce_unsized function into smaller helper functions
for better readability and maintainability.

gcc/rust/ChangeLog:

	* backend/rust-compile-base.h (resolve_adjustments): Fix typo.
	(resolve_unsized_adt_adjustment): New declaration.
	* backend/rust-compile-expr.cc (CompileExpr::visit): Fix typo.
	(HIRCompileBase::resolve_unsized_adt_adjustment): New function.
	(HIRCompileBase::resolve_unsized_adjustment): Use it.
	(CompileExpr::generate_possible_fn_trait_call): Likewise.
	(HIRCompileBase::coercion_site): Fix typo.
	* typecheck/rust-coercion.cc
	(TypeCoercionRules::coerce_unsized): Use helper functions.
	(TypeCoercionRules::unwrap_ptrs_and_refs): New function.
	(TypeCoercionRules::coerce_unsized_array_to_slice): Likewise.
	(TypeCoercionRules::coerce_unsized_dyn): Likewise.
	(TypeCoercionRules::coerce_unsized_adt): Liksewise.
	(TypeCoercionRules::apply_reborrow_adjustment): Likewise.
	* typecheck/rust-coercion.h (struct CoercionSetup): New struct.
	(coerce_unsized): Add is_inner parameter.
	(unwrap_ptrs_and_refs): New declaration.
	(coerce_unsized_array_to_slice): Likewise.
	(coerce_unsized_dyn): Likewise.
	(coerce_unsized_adt): Likewise.
	* util/rust-lang-item.cc (Rust::LangItem::lang_items): Add
	coerce_unsized to the BiMap.
	* util/rust-lang-item.h (class LangItem): Add COERCE_UNSIZED to
	the Kind enum.

gcc/testsuite/ChangeLog:

	* rust/compile/coercion2.rs: New test.

Signed-off-by: Enes Cevik <[email protected]>
---
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/d780d32bf97f1a0fcc2469c9b56f0404855e0446

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/4722

 gcc/rust/backend/rust-compile-base.h    |   7 +-
 gcc/rust/backend/rust-compile-expr.cc   | 116 ++++++++++-
 gcc/rust/typecheck/rust-coercion.cc     | 258 +++++++++++++++++-------
 gcc/rust/typecheck/rust-coercion.h      |  25 ++-
 gcc/rust/util/rust-lang-item.cc         |   1 +
 gcc/rust/util/rust-lang-item.h          |   3 +
 gcc/testsuite/rust/compile/coercion2.rs |  47 +++++
 7 files changed, 372 insertions(+), 85 deletions(-)
 create mode 100644 gcc/testsuite/rust/compile/coercion2.rs

diff --git a/gcc/rust/backend/rust-compile-base.h b/gcc/rust/backend/rust-compile-base.h
index d93cc3ba3..15356942d 100644
--- a/gcc/rust/backend/rust-compile-base.h
+++ b/gcc/rust/backend/rust-compile-base.h
@@ -76,8 +76,8 @@ protected:
 		      const Analysis::NodeMapping &mappings,
 		      location_t expr_locus, bool is_qualified_path);
 
-  tree resolve_adjustements (std::vector<Resolver::Adjustment> &adjustments,
-			     tree expression, location_t locus);
+  tree resolve_adjustments (std::vector<Resolver::Adjustment> &adjustments,
+			    tree expression, location_t locus);
 
   tree resolve_deref_adjustment (Resolver::Adjustment &adjustment,
 				 tree expression, location_t locus);
@@ -91,6 +91,9 @@ protected:
   tree resolve_unsized_slice_adjustment (Resolver::Adjustment &adjustment,
 					 tree expression, location_t locus);
 
+  tree resolve_unsized_adt_adjustment (Resolver::Adjustment &adjustment,
+				       tree expression, location_t locus);
+
   tree resolve_unsized_dyn_adjustment (Resolver::Adjustment &adjustment,
 				       tree expression, location_t locus);
 
diff --git a/gcc/rust/backend/rust-compile-expr.cc b/gcc/rust/backend/rust-compile-expr.cc
index 9eac66e8b..574099672 100644
--- a/gcc/rust/backend/rust-compile-expr.cc
+++ b/gcc/rust/backend/rust-compile-expr.cc
@@ -504,7 +504,7 @@ CompileExpr::visit (HIR::TypeCastExpr &expr)
   if (ok)
     {
       casted_expr
-	= resolve_adjustements (*adjustments, casted_expr, expr.get_locus ());
+	= resolve_adjustments (*adjustments, casted_expr, expr.get_locus ());
     }
 
   translated
@@ -1761,8 +1761,8 @@ CompileExpr::visit (HIR::MethodCallExpr &expr)
   rust_assert (ok);
 
   // apply adjustments for the fn call
-  self = resolve_adjustements (*adjustments, self,
-			       expr.get_receiver ().get_locus ());
+  self = resolve_adjustments (*adjustments, self,
+			      expr.get_receiver ().get_locus ());
 
   std::vector<tree> args;
   args.push_back (self); // adjusted self
@@ -1895,7 +1895,7 @@ CompileExpr::resolve_operator_overload (
   rust_assert (ok);
 
   // apply adjustments for the fn call
-  tree self = resolve_adjustements (*adjustments, lhs, lhs_expr.get_locus ());
+  tree self = resolve_adjustments (*adjustments, lhs, lhs_expr.get_locus ());
 
   std::vector<tree> args;
   args.push_back (self); // adjusted self
@@ -2398,7 +2398,7 @@ CompileExpr::array_copied_expr (location_t expr_locus,
 }
 
 tree
-HIRCompileBase::resolve_adjustements (
+HIRCompileBase::resolve_adjustments (
   std::vector<Resolver::Adjustment> &adjustments, tree expression,
   location_t locus)
 {
@@ -2492,6 +2492,8 @@ HIRCompileBase::resolve_unsized_adjustment (Resolver::Adjustment &adjustment,
     = adjustment.get_expected ()->get_kind () == TyTy::TypeKind::SLICE;
   bool expect_dyn
     = adjustment.get_expected ()->get_kind () == TyTy::TypeKind::DYNAMIC;
+  bool expect_adt
+    = adjustment.get_expected ()->get_kind () == TyTy::TypeKind::ADT;
 
   // assumes this is an array
   tree expr_type = TREE_TYPE (expression);
@@ -2501,6 +2503,9 @@ HIRCompileBase::resolve_unsized_adjustment (Resolver::Adjustment &adjustment,
       return resolve_unsized_slice_adjustment (adjustment, expression, locus);
     }
 
+  if (expect_adt)
+    return resolve_unsized_adt_adjustment (adjustment, expression, locus);
+
   rust_assert (expect_dyn);
   return resolve_unsized_dyn_adjustment (adjustment, expression, locus);
 }
@@ -2537,6 +2542,105 @@ HIRCompileBase::resolve_unsized_slice_adjustment (
 					  locus);
 }
 
+tree
+HIRCompileBase::resolve_unsized_adt_adjustment (
+  Resolver::Adjustment &adjustment, tree expression, location_t locus)
+{
+  /*
+   * FIXME: This method is implemented as a temporary workaround to enable the
+   * compilation of intra-ADT conversions for the `coerce_unsized` lang item.
+   * Currently, it generates incorrect GIMPLE, though it allows the compilation
+   * to succeed. Execution tests relying on this will exhibit undefined behavior
+   * at runtime. This must be revisited and properly refactored once the DST
+   * memory layout is fully supported.
+   */
+
+  auto source_adt
+    = static_cast<const TyTy::ADTType *> (adjustment.get_actual ());
+  auto target_adt
+    = static_cast<const TyTy::ADTType *> (adjustment.get_expected ());
+
+  auto s_variant = source_adt->get_variants ().front ();
+  auto t_variant = target_adt->get_variants ().front ();
+
+  std::vector<tree> constructor_elements;
+
+  for (size_t i = 0; i < s_variant->num_fields (); i++)
+    {
+      auto s_field_ty = s_variant->get_field_at_index (i)
+			  ->get_field_type ()
+			  ->monomorphized_clone ();
+      auto t_field_ty = t_variant->get_field_at_index (i)
+			  ->get_field_type ()
+			  ->monomorphized_clone ();
+
+      tree field_expr = Backend::struct_field_expression (expression, i, locus);
+      if (s_field_ty->is_equal (*t_field_ty))
+	constructor_elements.push_back (field_expr);
+      else
+	{
+	  bool is_ptr = s_field_ty->get_kind () == TyTy::TypeKind::POINTER;
+	  bool is_ref = s_field_ty->get_kind () == TyTy::TypeKind::REF;
+
+	  if (is_ptr || is_ref)
+	    {
+	      TyTy::BaseType *s_base = nullptr;
+	      TyTy::BaseType *t_base = nullptr;
+	      Resolver::Adjustment::AdjustmentType ref_adj_type
+		= Resolver::Adjustment::AdjustmentType::IMM_REF;
+
+	      if (is_ptr)
+		{
+		  auto s_ptr
+		    = static_cast<const TyTy::PointerType *> (s_field_ty);
+		  auto t_ptr
+		    = static_cast<const TyTy::PointerType *> (t_field_ty);
+		  s_base = s_ptr->get_base ();
+		  t_base = t_ptr->get_base ();
+		  if (t_ptr->mutability () == Mutability::Mut)
+		    ref_adj_type
+		      = Resolver::Adjustment::AdjustmentType::MUT_REF;
+		}
+	      else
+		{
+		  auto s_ref
+		    = static_cast<const TyTy::ReferenceType *> (s_field_ty);
+		  auto t_ref
+		    = static_cast<const TyTy::ReferenceType *> (t_field_ty);
+		  s_base = s_ref->get_base ();
+		  t_base = t_ref->get_base ();
+		  if (t_ref->mutability () == Mutability::Mut)
+		    ref_adj_type
+		      = Resolver::Adjustment::AdjustmentType::MUT_REF;
+		}
+	      std::vector<Resolver::Adjustment> inner_adjs;
+	      inner_adjs.push_back (Resolver::Adjustment (
+		Resolver::Adjustment::AdjustmentType::INDIRECTION, s_field_ty,
+		s_base));
+	      inner_adjs.push_back (Resolver::Adjustment (
+		Resolver::Adjustment::AdjustmentType::UNSIZE, s_base, t_base));
+	      inner_adjs.push_back (
+		Resolver::Adjustment (ref_adj_type, t_base, t_field_ty));
+
+	      tree coerced_ptr
+		= resolve_adjustments (inner_adjs, field_expr, locus);
+	      constructor_elements.push_back (coerced_ptr);
+	    }
+	  else
+	    {
+	      Resolver::Adjustment inner_adj (adjustment.get_type (),
+					      s_field_ty, t_field_ty);
+	      tree unsized_inner_expr
+		= resolve_unsized_adjustment (inner_adj, field_expr, locus);
+	      constructor_elements.push_back (unsized_inner_expr);
+	    }
+	}
+    }
+  tree target_type_tree = TyTyResolveCompile::compile (ctx, target_adt);
+  return Backend::constructor_expression (target_type_tree, false,
+					  constructor_elements, -1, locus);
+}
+
 tree
 HIRCompileBase::resolve_unsized_dyn_adjustment (
   Resolver::Adjustment &adjustment, tree expression, location_t locus)
@@ -2987,7 +3091,7 @@ CompileExpr::generate_possible_fn_trait_call (HIR::CallExpr &expr,
   rust_assert (ok);
 
   // apply adjustments for the fn call
-  tree self = resolve_adjustements (*adjustments, receiver, expr.get_locus ());
+  tree self = resolve_adjustments (*adjustments, receiver, expr.get_locus ());
 
   // resolve the arguments
   std::vector<tree> tuple_arg_vals;
diff --git a/gcc/rust/typecheck/rust-coercion.cc b/gcc/rust/typecheck/rust-coercion.cc
index a423449ba..fab689c24 100644
--- a/gcc/rust/typecheck/rust-coercion.cc
+++ b/gcc/rust/typecheck/rust-coercion.cc
@@ -316,17 +316,80 @@ TypeCoercionRules::coerce_borrowed_pointer (TyTy::BaseType *receiver,
 tl::expected<TypeCoercionRules::CoercionResult,
 	     TypeCoercionRules::CoerceUnsizedError>
 TypeCoercionRules::coerce_unsized (TyTy::BaseType *source,
-				   TyTy::BaseType *target)
+				   TyTy::BaseType *target, bool is_inner)
 {
   rust_debug ("coerce_unsized(source={%s}, target={%s})",
 	      source->debug_str ().c_str (), target->debug_str ().c_str ());
+  size_t adjustments_size = adjustments.size ();
 
+  auto setup = unwrap_ptrs_and_refs (source, target);
+  if (!setup)
+    return tl::unexpected<CoerceUnsizedError> (setup.error ());
+
+  // FIXME
+  // there is a bunch of code to ensure something is coerce able to a dyn
+  // trait we need to support but we need to support a few more lang items for
+  // that see:
+  // https://github.com/rust-lang/rust/blob/7eac88abb2e57e752f3302f02be5f3ce3d7adfb4/compiler/rustc_typeck/src/check/coercion.rs#L582
+
+  auto a = setup->ty_a;
+  auto b = setup->ty_b;
+
+  tl::expected<TyTy::BaseType *, CoerceUnsizedError> inner_result
+    = tl::unexpected (CoerceUnsizedError::Regular);
+
+  bool expect_dyn = b->get_kind () == TyTy::TypeKind::DYNAMIC;
+  bool need_unsize = a->get_kind () != TyTy::TypeKind::DYNAMIC;
+
+  bool expect_slice = b->get_kind () == TyTy::TypeKind::SLICE;
+  bool is_array = a->get_kind () == TyTy::TypeKind::ARRAY;
+
+  bool expect_adt = b->get_kind () == TyTy::TypeKind::ADT;
+  bool is_adt = a->get_kind () == TyTy::TypeKind::ADT;
+
+  if (expect_dyn && need_unsize)
+    inner_result
+      = (!setup->unwrapped_pointer && !is_inner)
+	  ? tl::unexpected<CoerceUnsizedError> (inner_result.error ())
+	  : coerce_unsized_dyn (a, b);
+
+  else if (expect_slice && is_array)
+    inner_result
+      = (!setup->unwrapped_pointer && !is_inner)
+	  ? tl::unexpected<CoerceUnsizedError> (inner_result.error ())
+	  : coerce_unsized_array_to_slice (a, b);
+
+  else if (expect_adt && is_adt)
+    inner_result = coerce_unsized_adt (a, b, setup->needs_reborrow);
+
+  if (!inner_result)
+    {
+      adjustments.erase (adjustments.begin () + adjustments_size,
+			 adjustments.end ());
+      return tl::unexpected<CoerceUnsizedError> (inner_result.error ());
+    }
+
+  TyTy::BaseType *result = inner_result.value ();
+
+  if (setup->needs_reborrow)
+    result = apply_reborrow_adjustment (source, target, result,
+					setup->expected_mutability);
+
+  return CoercionResult{adjustments, result};
+}
+
+tl::expected<TypeCoercionRules::CoercionSetup,
+	     TypeCoercionRules::CoerceUnsizedError>
+TypeCoercionRules::unwrap_ptrs_and_refs (TyTy::BaseType *source,
+					 TyTy::BaseType *target)
+{
   bool source_is_ref = source->get_kind () == TyTy::TypeKind::REF;
   bool source_is_ptr = source->get_kind () == TyTy::TypeKind::POINTER;
   bool target_is_ref = target->get_kind () == TyTy::TypeKind::REF;
   bool target_is_ptr = target->get_kind () == TyTy::TypeKind::POINTER;
 
   bool needs_reborrow = false;
+  bool unwrapped_pointer = false;
   TyTy::BaseType *ty_a = source;
   TyTy::BaseType *ty_b = target;
   Mutability expected_mutability = Mutability::Imm;
@@ -351,6 +414,7 @@ TypeCoercionRules::coerce_unsized (TyTy::BaseType *source,
       ty_a = source_ref->get_base ();
       ty_b = target_ref->get_base ();
       needs_reborrow = true;
+      unwrapped_pointer = true;
       expected_mutability = to_mutbl;
 
       adjustments.emplace_back (Adjustment::AdjustmentType::INDIRECTION,
@@ -376,6 +440,7 @@ TypeCoercionRules::coerce_unsized (TyTy::BaseType *source,
       ty_a = source_ref->get_base ();
       ty_b = target_ref->get_base ();
       needs_reborrow = true;
+      unwrapped_pointer = true;
       expected_mutability = to_mutbl;
 
       adjustments.emplace_back (Adjustment::AdjustmentType::INDIRECTION,
@@ -400,104 +465,145 @@ TypeCoercionRules::coerce_unsized (TyTy::BaseType *source,
       ty_a = source_ref->get_base ();
       ty_b = target_ref->get_base ();
       needs_reborrow = true;
+      unwrapped_pointer = true;
       expected_mutability = to_mutbl;
 
       adjustments.emplace_back (Adjustment::AdjustmentType::INDIRECTION,
 				source_ref, ty_a);
     }
 
-  // FIXME
-  // there is a bunch of code to ensure something is coerce able to a dyn trait
-  // we need to support but we need to support a few more lang items for that
-  // see:
-  // https://github.com/rust-lang/rust/blob/7eac88abb2e57e752f3302f02be5f3ce3d7adfb4/compiler/rustc_typeck/src/check/coercion.rs#L582
+  return CoercionSetup{ty_a, ty_b, needs_reborrow, expected_mutability,
+		       unwrapped_pointer};
+}
+
+tl::expected<TyTy::BaseType *, TypeCoercionRules::CoerceUnsizedError>
+TypeCoercionRules::coerce_unsized_array_to_slice (TyTy::BaseType *a,
+						  TyTy::BaseType *b)
+{
+  auto array_type = static_cast<const TyTy::ArrayType *> (a);
+  auto slice_type = static_cast<const TyTy::SliceType *> (b);
 
-  const auto a = ty_a;
-  const auto b = ty_b;
+  TyTy::BaseType *array_element = array_type->get_element_type ();
+  TyTy::BaseType *slice_element = slice_type->get_element_type ();
 
-  bool expect_dyn = b->get_kind () == TyTy::TypeKind::DYNAMIC;
-  bool need_unsize = a->get_kind () != TyTy::TypeKind::DYNAMIC;
+  if (!array_element->is_equal (*slice_element))
+    return tl::unexpected<CoerceUnsizedError> (CoerceUnsizedError::Regular);
 
-  bool expect_slice = b->get_kind () == TyTy::TypeKind::SLICE;
-  bool is_array = a->get_kind () == TyTy::TypeKind::ARRAY;
+  TyTy::BaseType *result = b->clone ();
 
-  if (expect_dyn && need_unsize)
-    {
-      bool bounds_compatible = b->bounds_compatible (*a, locus, false);
-      if (!bounds_compatible)
-	return tl::unexpected<CoerceUnsizedError> (CoerceUnsizedError::Unsafe);
+  adjustments.emplace_back (Adjustment::UNSIZE, a, result);
+
+  return result;
+}
+tl::expected<TyTy::BaseType *, TypeCoercionRules::CoerceUnsizedError>
+TypeCoercionRules::coerce_unsized_dyn (TyTy::BaseType *a, TyTy::BaseType *b)
+{
+  bool bounds_compatible = b->bounds_compatible (*a, locus, false);
+  if (!bounds_compatible)
+    return tl::unexpected<CoerceUnsizedError> (CoerceUnsizedError::Unsafe);
+
+  // return the unsize coercion
+  TyTy::BaseType *result = b->clone ();
+  // result->set_ref (a->get_ref ());
+
+  // append a dyn coercion adjustment
+  adjustments.emplace_back (Adjustment::UNSIZE, a, result);
+
+  return result;
+}
+tl::expected<TyTy::BaseType *, TypeCoercionRules::CoerceUnsizedError>
+TypeCoercionRules::coerce_unsized_adt (TyTy::BaseType *a, TyTy::BaseType *b,
+				       bool needs_reborrow)
+{
+  auto source_adt = static_cast<const TyTy::ADTType *> (a);
+  auto target_adt = static_cast<const TyTy::ADTType *> (b);
+
+  if ((!source_adt->is_struct_struct () && !source_adt->is_tuple_struct ())
+      || (!target_adt->is_struct_struct () && !target_adt->is_tuple_struct ())
+      || (source_adt->get_id () != target_adt->get_id ())
+      || (source_adt->get_variants ().front ()->num_fields ()
+	  != target_adt->get_variants ().front ()->num_fields ()))
+    return tl::unexpected<CoerceUnsizedError> (CoerceUnsizedError::Regular);
 
-      // return the unsize coercion
-      TyTy::BaseType *result = b->clone ();
-      // result->set_ref (a->get_ref ());
+  auto source_variant = source_adt->get_variants ().front ();
+  auto target_variant = target_adt->get_variants ().front ();
 
-      // append a dyn coercion adjustment
-      adjustments.emplace_back (Adjustment::UNSIZE, a, result);
+  TyTy::BaseType *differing_source_field = nullptr;
+  TyTy::BaseType *differing_target_field = nullptr;
+  size_t diff_count = 0;
+  bool is_last_field = false;
 
-      // reborrow if needed
-      if (needs_reborrow)
+  for (size_t i = 0; i < source_variant->num_fields (); i++)
+    {
+      auto s_field_raw
+	= source_variant->get_field_at_index (i)->get_field_type ();
+      auto t_field_raw
+	= target_variant->get_field_at_index (i)->get_field_type ();
+      auto s_field = s_field_raw->contains_infer ()
+		       ? s_field_raw
+		       : s_field_raw->monomorphized_clone ();
+      auto t_field = t_field_raw->contains_infer ()
+		       ? t_field_raw
+		       : t_field_raw->monomorphized_clone ();
+
+      if (s_field->is_zero_sized () && t_field->is_zero_sized ())
+	continue;
+
+      if (!s_field->is_equal (*t_field))
 	{
-	  TyTy::ReferenceType *reborrow
-	    = new TyTy::ReferenceType (source->get_ref (),
-				       TyTy::TyVar (result->get_ref ()),
-				       expected_mutability);
-
-	  Adjustment::AdjustmentType borrow_type
-	    = expected_mutability == Mutability::Imm ? Adjustment::IMM_REF
-						     : Adjustment::MUT_REF;
-	  adjustments.emplace_back (borrow_type, result, reborrow);
-	  result = reborrow;
+	  differing_source_field = s_field;
+	  differing_target_field = t_field;
+	  diff_count++;
+	  is_last_field = (i == source_variant->num_fields () - 1);
 	}
-      return CoercionResult{adjustments, result};
     }
-  else if (expect_slice && is_array)
-    {
-      auto array_type = static_cast<const TyTy::ArrayType *> (a);
-      auto slice_type = static_cast<const TyTy::SliceType *> (b);
 
-      TyTy::BaseType *array_element = array_type->get_element_type ();
-      TyTy::BaseType *slice_element = slice_type->get_element_type ();
+  if (diff_count != 1)
+    return tl::unexpected<CoerceUnsizedError> (CoerceUnsizedError::Regular);
 
-      if (!array_element->is_equal (*slice_element))
-	{
-	  adjustments.clear ();
-	  return tl::unexpected<CoerceUnsizedError> (
-	    CoerceUnsizedError::Regular);
-	}
-      TyTy::BaseType *result = b->clone ();
+  if (needs_reborrow && !is_last_field)
+    return tl::unexpected<CoerceUnsizedError> (CoerceUnsizedError::Regular);
 
-      adjustments.emplace_back (Adjustment::UNSIZE, a, result);
+  auto adjustments_size = adjustments.size ();
+  auto inner_coercion
+    = coerce_unsized (differing_source_field, differing_target_field, true);
+  if (!inner_coercion)
+    return tl::unexpected<CoerceUnsizedError> (inner_coercion.error ());
+  adjustments.erase (adjustments.begin () + adjustments_size,
+		     adjustments.end ());
 
-      if (needs_reborrow)
-	{
-	  TyTy::BaseType *reborrow = nullptr;
-	  if (target->get_kind () == TyTy::TypeKind::POINTER)
-	    {
-	      reborrow
-		= new TyTy::PointerType (source->get_ref (),
-					 TyTy::TyVar (result->get_ref ()),
-					 expected_mutability);
-	    }
-	  else
-	    {
-	      reborrow
-		= new TyTy::ReferenceType (source->get_ref (),
-					   TyTy::TyVar (result->get_ref ()),
-					   expected_mutability);
-	    }
-
-	  Adjustment::AdjustmentType borrow_type
-	    = expected_mutability == Mutability::Imm ? Adjustment::IMM_REF
-						     : Adjustment::MUT_REF;
-	  adjustments.emplace_back (borrow_type, result, reborrow);
-	  result = reborrow;
-	}
+  TyTy::BaseType *result = b->clone ();
+  adjustments.emplace_back (Adjustment::UNSIZE, a, result);
 
-      return CoercionResult{adjustments, result};
+  return result;
+}
+TyTy::BaseType *
+TypeCoercionRules::apply_reborrow_adjustment (TyTy::BaseType *source,
+					      TyTy::BaseType *target,
+					      TyTy::BaseType *result,
+					      Mutability expected_mutability)
+{
+  TyTy::BaseType *reborrow = nullptr;
+
+  if (target->get_kind () == TyTy::TypeKind::POINTER)
+    {
+      reborrow = new TyTy::PointerType (source->get_ref (),
+					TyTy::TyVar (result->get_ref ()),
+					expected_mutability);
     }
+  else
+    {
+      reborrow = new TyTy::ReferenceType (source->get_ref (),
+					  TyTy::TyVar (result->get_ref ()),
+					  expected_mutability);
+    }
+
+  Adjustment::AdjustmentType borrow_type
+    = expected_mutability == Mutability::Imm ? Adjustment::IMM_REF
+					     : Adjustment::MUT_REF;
 
-  adjustments.clear ();
-  return tl::unexpected<CoerceUnsizedError> (CoerceUnsizedError::Regular);
+  adjustments.emplace_back (borrow_type, result, reborrow);
+  return reborrow;
 }
 
 bool
diff --git a/gcc/rust/typecheck/rust-coercion.h b/gcc/rust/typecheck/rust-coercion.h
index d52f61051..0674cf5df 100644
--- a/gcc/rust/typecheck/rust-coercion.h
+++ b/gcc/rust/typecheck/rust-coercion.h
@@ -68,7 +68,8 @@ public:
 					  Mutability mutability);
 
   tl::expected<CoercionResult, CoerceUnsizedError>
-  coerce_unsized (TyTy::BaseType *receiver, TyTy::BaseType *expected);
+  coerce_unsized (TyTy::BaseType *receiver, TyTy::BaseType *expected,
+		  bool is_inner = false);
 
   static bool coerceable_mutability (Mutability from_mutbl,
 				     Mutability to_mutbl);
@@ -87,6 +88,28 @@ protected:
 
   bool do_coercion (TyTy::BaseType *receiver);
 
+  struct CoercionSetup
+  {
+    TyTy::BaseType *ty_a;
+    TyTy::BaseType *ty_b;
+    bool needs_reborrow;
+    Mutability expected_mutability;
+    bool unwrapped_pointer;
+  };
+  tl::expected<CoercionSetup, CoerceUnsizedError>
+  unwrap_ptrs_and_refs (TyTy::BaseType *source, TyTy::BaseType *target);
+  tl::expected<TyTy::BaseType *, CoerceUnsizedError>
+  coerce_unsized_array_to_slice (TyTy::BaseType *a, TyTy::BaseType *b);
+  tl::expected<TyTy::BaseType *, CoerceUnsizedError>
+  coerce_unsized_dyn (TyTy::BaseType *a, TyTy::BaseType *b);
+  tl::expected<TyTy::BaseType *, CoerceUnsizedError>
+  coerce_unsized_adt (TyTy::BaseType *a, TyTy::BaseType *b,
+		      bool needs_reborrow);
+  TyTy::BaseType *apply_reborrow_adjustment (TyTy::BaseType *source,
+					     TyTy::BaseType *target,
+					     TyTy::BaseType *result,
+					     Mutability expected_mutability);
+
 private:
   // context info
   Analysis::Mappings &mappings;
diff --git a/gcc/rust/util/rust-lang-item.cc b/gcc/rust/util/rust-lang-item.cc
index a54b2e594..3be15a805 100644
--- a/gcc/rust/util/rust-lang-item.cc
+++ b/gcc/rust/util/rust-lang-item.cc
@@ -56,6 +56,7 @@ const BiMap<std::string, LangItem::Kind> Rust::LangItem::lang_items = {{
   {"RangeInclusive", Kind::RANGE_INCLUSIVE},
   {"RangeToInclusive", Kind::RANGE_TO_INCLUSIVE},
   {"range_inclusive_new", Kind::RANGE_INCLUSIVE_NEW},
+  {"coerce_unsized", Kind::COERCE_UNSIZED},
   {"phantom_data", Kind::PHANTOM_DATA},
   {"fn", Kind::FN},
   {"fn_mut", Kind::FN_MUT},
diff --git a/gcc/rust/util/rust-lang-item.h b/gcc/rust/util/rust-lang-item.h
index 476752367..8cadfd0c2 100644
--- a/gcc/rust/util/rust-lang-item.h
+++ b/gcc/rust/util/rust-lang-item.h
@@ -79,6 +79,9 @@ public:
     RANGE_TO_INCLUSIVE,
     RANGE_INCLUSIVE_NEW,
 
+    // https://github.com/rust-lang/rust/blob/master/library/core/src/ops/unsize.rs
+    COERCE_UNSIZED,
+
     // https://github.com/rust-lang/rust/blob/master/library/core/src/marker.rs
     PHANTOM_DATA,
 
diff --git a/gcc/testsuite/rust/compile/coercion2.rs b/gcc/testsuite/rust/compile/coercion2.rs
new file mode 100644
index 000000000..d852f3f04
--- /dev/null
+++ b/gcc/testsuite/rust/compile/coercion2.rs
@@ -0,0 +1,47 @@
+#![feature(no_core, lang_items)]
+#![no_core]
+
+#[lang = "sized"]
+pub trait Sized {}
+
+#[lang = "phantom_data"]
+pub struct PhantomData<T: ?Sized>;
+
+#[lang = "coerce_unsized"]
+pub trait CoerceUnsized<T: ?Sized> {
+    // This lang item is not used for now.
+}
+
+pub struct NonNull<T: ?Sized> {
+    _ptr: *const T,
+}
+
+pub struct Unique<T: ?Sized> {
+    _pointer: NonNull<T>,
+    _marker: PhantomData<T>,
+}
+
+pub struct MyBox<T: ?Sized> {
+    _inner: Unique<T>,
+}
+
+pub struct TailStruct<T: ?Sized> {
+    _header: usize,
+    _data: T,
+}
+
+pub fn test_nested_coercions() {
+    let tail: TailStruct<[i32; 3]> = TailStruct { _header: 1, _data: [1, 2, 3] };
+    let tail_ptr: *const TailStruct<[i32; 3]> = &tail;
+    
+    let non_null = NonNull::<TailStruct<[i32; 3]>> { _ptr: tail_ptr };
+    
+    let unique = Unique::<TailStruct<[i32; 3]>> { 
+        _pointer: non_null, 
+        _marker: PhantomData::<TailStruct<[i32; 3]>> 
+    };
+    
+    let my_box = MyBox::<TailStruct<[i32; 3]>> { _inner: unique };
+
+    let _my_box_slice: MyBox<TailStruct<[i32]>> = my_box;
+}
-- 
2.54.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.