From: Enes Cevik <[email protected]>

This patch implements the 'unsize' lang item to the compiler.

gcc/rust/ChangeLog:

        * typecheck/rust-tyty.cc (BaseType::unsize_to): New function.
        (BaseType::satisfies_bound): Use it.
        * typecheck/rust-tyty.h (class BaseType): New declaration.
        * util/rust-lang-item.cc (Rust::LangItem::lang_items): Add
        unsize to the BiMap.
        * util/rust-lang-item.h (class LangItem): Add UNSIZE to the Kind
        enum.

gcc/testsuite/ChangeLog:

        * rust/compile/unsize2.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/942b257486195eaafdff33dad3839b8605ef48f6

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/typecheck/rust-tyty.cc       | 91 +++++++++++++++++++++++++++
 gcc/rust/typecheck/rust-tyty.h        |  2 +
 gcc/rust/util/rust-lang-item.cc       |  1 +
 gcc/rust/util/rust-lang-item.h        |  1 +
 gcc/testsuite/rust/compile/unsize2.rs | 40 ++++++++++++
 5 files changed, 135 insertions(+)
 create mode 100644 gcc/testsuite/rust/compile/unsize2.rs

diff --git a/gcc/rust/typecheck/rust-tyty.cc b/gcc/rust/typecheck/rust-tyty.cc
index 65130ea25..72b2f9d7a 100644
--- a/gcc/rust/typecheck/rust-tyty.cc
+++ b/gcc/rust/typecheck/rust-tyty.cc
@@ -288,10 +288,101 @@ BaseType::get_locus () const
   return ident.locus;
 }
 
+bool
+BaseType::unsize_to (const BaseType *target) const
+{
+  if (this->get_kind () == TyTy::TypeKind::DYNAMIC
+      && target->get_kind () == TyTy::TypeKind::DYNAMIC)
+    {
+      const auto *source_dyn = this->as<const TyTy::DynamicObjectType> ();
+      const auto *target_dyn = target->as<const TyTy::DynamicObjectType> ();
+
+      const auto &source_bounds = source_dyn->get_specified_bounds ();
+      const auto &target_bounds = target_dyn->get_specified_bounds ();
+
+      if (source_bounds.empty () || target_bounds.empty ())
+       return false;
+
+      if (source_bounds.at (0).get_id () != target_bounds.at (0).get_id ())
+       return false;
+
+      for (const auto &t_bound : target_dyn->get_specified_bounds ())
+       {
+         bool found = false;
+         for (const auto &s_bound : source_dyn->get_specified_bounds ())
+           {
+             if (s_bound.get_id () == t_bound.get_id ())
+               {
+                 found = true;
+                 break;
+               }
+           }
+         if (!found)
+           return false;
+       }
+      return true;
+    }
+
+  // `T` -> `Trait`
+  else if (target->get_kind () == TyTy::TypeKind::DYNAMIC)
+    return true;
+
+  // Ambiguous handling is below `T` -> `Trait`, because inference
+  // variables can still implement `Unsize<Trait>` and nested
+  // obligations will have the final say (likely deferred).
+  else if (this->destructure ()->is<InferType> ()
+          || target->destructure ()->is<InferType> ())
+    return true;
+
+  // `[T; n]` -> `[T]`
+  else if (this->get_kind () == TyTy::TypeKind::ARRAY
+          && target->get_kind () == TyTy::TypeKind::SLICE)
+    return true;
+
+  // `Struct<T>` -> `Struct<U>`
+  else if (this->get_kind () == TyTy::TypeKind::ADT
+          && target->get_kind () == TyTy::TypeKind::ADT)
+    {
+      const auto *source_adt = this->as<const TyTy::ADTType> ();
+      const auto *target_adt = target->as<const TyTy::ADTType> ();
+      return (source_adt->is_struct_struct () || source_adt->is_tuple_struct 
())
+            && source_adt->get_id () == target_adt->get_id ();
+    }
+
+  // `(.., T)` -> `(.., U)`
+  else if (this->get_kind () == TyTy::TypeKind::TUPLE
+          && target->get_kind () == TyTy::TypeKind::TUPLE)
+    {
+      const auto *source_tuple = this->as<const TyTy::TupleType> ();
+      const auto *target_tuple = target->as<const TyTy::TupleType> ();
+      return source_tuple->get_subst_argument_mappings ().size ()
+            == target_tuple->get_subst_argument_mappings ().size ();
+    }
+  return false;
+}
+
 // FIXME this is missing locus
 bool
 BaseType::satisfies_bound (const TypeBoundPredicate &predicate, bool 
emit_error)
 {
+  // see:
+  // 
https://github.com/rust-lang/rust/blob/e1884a8e3c3e813aada8254edfa120e85bf5ffca/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs#L673
+  if (auto unsize_id = mappings.lookup_lang_item 
(Rust::LangItem::Kind::UNSIZE))
+    {
+      if (predicate.get_id () == unsize_id)
+       {
+         const auto &args = predicate.get_substitution_arguments ();
+         rust_assert (args.size () == 2 && "Unsize<U>");
+
+         // When this target is defined as `U`, it does not give us the
+         // realized argument type. Instead, it returns `PARAM`.
+         TyTy::BaseType *target
+           = args.get_mappings ().at (1).get_param_ty ()->resolve ();
+
+         return this->unsize_to (target);
+       }
+    }
+
   const Resolver::TraitReference *query = predicate.get ();
   for (const auto &bound : specified_bounds)
     {
diff --git a/gcc/rust/typecheck/rust-tyty.h b/gcc/rust/typecheck/rust-tyty.h
index d607c6b49..ecb2b3e24 100644
--- a/gcc/rust/typecheck/rust-tyty.h
+++ b/gcc/rust/typecheck/rust-tyty.h
@@ -237,6 +237,8 @@ public:
   //     2. (For functions) have the same signature
   virtual bool is_equal (const BaseType &other) const;
 
+  bool unsize_to (const BaseType *target) const;
+
   bool satisfies_bound (const TypeBoundPredicate &predicate, bool emit_error);
 
   bool bounds_compatible (BaseType &other, location_t locus, bool emit_error);
diff --git a/gcc/rust/util/rust-lang-item.cc b/gcc/rust/util/rust-lang-item.cc
index 5a3e939f9..a54b2e594 100644
--- a/gcc/rust/util/rust-lang-item.cc
+++ b/gcc/rust/util/rust-lang-item.cc
@@ -66,6 +66,7 @@ const BiMap<std::string, LangItem::Kind> 
Rust::LangItem::lang_items = {{
   {"drop", Kind::DROP},
   {"sized", Kind::SIZED},
   {"freeze", Kind::FREEZE},
+  {"unsize", Kind::UNSIZE},
   {"sync", Kind::SYNC},
   {"slice_alloc", Kind::SLICE_ALLOC},
   {"slice_u8_alloc", Kind::SLICE_U8_ALLOC},
diff --git a/gcc/rust/util/rust-lang-item.h b/gcc/rust/util/rust-lang-item.h
index 766b47c79..476752367 100644
--- a/gcc/rust/util/rust-lang-item.h
+++ b/gcc/rust/util/rust-lang-item.h
@@ -94,6 +94,7 @@ public:
     DROP,
     SIZED,
     FREEZE,
+    UNSIZE,
     SYNC,
 
     // https://github.com/Rust-GCC/gccrs/issues/1896
diff --git a/gcc/testsuite/rust/compile/unsize2.rs 
b/gcc/testsuite/rust/compile/unsize2.rs
new file mode 100644
index 000000000..609380268
--- /dev/null
+++ b/gcc/testsuite/rust/compile/unsize2.rs
@@ -0,0 +1,40 @@
+#![feature(no_core, lang_items, optin_builtin_traits)]
+#![no_core]
+
+#[lang = "sized"]
+pub trait Sized {}
+
+#[lang = "unsize"]
+pub trait Unsize<T: ?Sized> {}
+
+fn unsize_array<T: ?Sized>() where T: Unsize<[i32]> {}
+fn unsize_trait<T: ?Sized>() where T: Unsize<dyn Animal> {}
+fn unsize_dyn<T: ?Sized>() where T: Unsize<dyn Animal> {}
+fn unsize_struct<T: ?Sized>() where T: Unsize<_MyWrapper<[i32]>> {}
+fn unsize_tuple<T: ?Sized>() where T: Unsize<(i32, [i32])> {}
+
+trait Animal {}
+struct _Dog;
+impl Animal for _Dog {}
+auto trait Send {}
+
+struct _MyWrapper<T: ?Sized> {
+    pub data: T,
+}
+
+fn main() {
+    // 1. ARRAY -> SLICE
+    unsize_array::<[i32; 3]>();
+
+    // 2. T -> dyn Trait
+    unsize_trait::<_Dog>();
+
+    // 3. Dynamic -> Dynamic
+    unsize_dyn::<dyn Animal + Send>();
+
+    // 4. Struct -> Struct
+    unsize_struct::<_MyWrapper<[i32; 3]>>();
+
+    // 5. Tuple -> Tuple
+    unsize_tuple::<(i32, [i32; 3])>();
+}

base-commit: 7a3c1de74ae6e3cf18b3ccf81f1a9f356f3e5c74
-- 
2.54.0

Reply via email to