https://gcc.gnu.org/g:88ff00bf08303bb4e0346b35984f59edd40081cd
commit r17-4108-g88ff00bf08303bb4e0346b35984f59edd40081cd Author: Enes Cevik <[email protected]> Date: Fri Jul 17 14:40:57 2026 +0300 gccrs: lang: Add unsize 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]> Diff: --- 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(+) diff --git a/gcc/rust/typecheck/rust-tyty.cc b/gcc/rust/typecheck/rust-tyty.cc index 65130ea25303..72b2f9d7a47f 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 d607c6b49633..ecb2b3e244de 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 5a3e939f93a4..a54b2e594609 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 766b47c79464..476752367b91 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 000000000000..609380268b67 --- /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])>(); +}
