[gccrs COMMIT 1/6] lang: Add unsize
gerris.rs@gmail.com
gerris.rs@gmail.com
Sat Aug 15 21:42:12 GMT 2026
From: Enes Cevik <enes@nsvke.com>
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 <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/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
More information about the Gcc-rust
mailing list