]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
gccrs: add discriminant value intrinsic
authorPhilip Herron <herron.philip@googlemail.com>
Mon, 10 Feb 2025 16:28:30 +0000 (16:28 +0000)
committerArthur Cohen <arthur.cohen@embecosm.com>
Mon, 24 Mar 2025 12:07:04 +0000 (13:07 +0100)
This is pretty nasty piece of rust in my opinion the return type of this
intrinsic results into a lang item associated type:

  <T as DiscriminantKind>::Discriminant

This is a special case which needs to support mapping onto the repr type
of the associated ADT that is passed in, but defaults to iszie otherwise.

This patch assumes we only come accross this case in a HIR::CALL_EXPR, so
and makes assumutions that its always of this function signiture. I will
do some checking in libcore to verify that assumption. More work is needed
to parse the repr type on enums but the code is there to support this when
its in to change the types etc.

Addresses Rust-GCC#3348

gcc/rust/ChangeLog:

* backend/rust-compile-intrinsic.cc (discriminant_value_handler): new handler
* typecheck/rust-hir-trait-resolve.cc (TraitItemReference::resolve_item): track the defid
* typecheck/rust-hir-type-check-base.cc (TypeCheckBase::parse_repr_options): default isize
* typecheck/rust-hir-type-check-expr.cc (TypeCheckExpr::visit): special case CallExpr
* typecheck/rust-hir-type-check-item.cc (TypeCheckItem::visit): parse repr options enum
* typecheck/rust-hir-type-check-type.cc (TypeCheckType::visit): remove bad diagnostic
* typecheck/rust-tyty.cc (PlaceholderType::PlaceholderType): track defid
(PlaceholderType::clone): likewise
(PlaceholderType::get_def_id): likeiwse
* typecheck/rust-tyty.h: placeholder track defid
* util/rust-lang-item.cc: add new lang items
* util/rust-lang-item.h: likewise

gcc/testsuite/ChangeLog:

* rust/execute/torture/enum_intrinsics1.rs: New test.

Signed-off-by: Philip Herron <herron.philip@googlemail.com>
gcc/rust/backend/rust-compile-intrinsic.cc
gcc/rust/typecheck/rust-hir-trait-resolve.cc
gcc/rust/typecheck/rust-hir-type-check-base.cc
gcc/rust/typecheck/rust-hir-type-check-expr.cc
gcc/rust/typecheck/rust-hir-type-check-item.cc
gcc/rust/typecheck/rust-hir-type-check-type.cc
gcc/rust/typecheck/rust-tyty.cc
gcc/rust/typecheck/rust-tyty.h
gcc/rust/util/rust-lang-item.cc
gcc/rust/util/rust-lang-item.h
gcc/testsuite/rust/execute/torture/enum_intrinsics1.rs [new file with mode: 0644]

index 0f05458822077164186a6e29784ee2c5a54dd58f..1f93e82a93a95ba98e191a34dd44961f4b4d0a49 100644 (file)
@@ -17,7 +17,6 @@
 #include "rust-compile-intrinsic.h"
 #include "rust-compile-context.h"
 #include "rust-compile-type.h"
-#include "rust-compile-expr.h"
 #include "rust-compile-fnparam.h"
 #include "rust-builtins.h"
 #include "rust-diagnostics.h"
 #include "rust-tree.h"
 #include "tree-core.h"
 #include "rust-gcc.h"
-#include "print-tree.h"
 #include "fold-const.h"
 #include "langhooks.h"
-#include "rust-gcc.h"
 #include "rust-constexpr.h"
 
-#include "print-tree.h"
-
 // declaration taken from "stringpool.h"
 // the get_identifier macro causes compilation issues
 extern tree
@@ -93,6 +88,8 @@ static tree
 move_val_init_handler (Context *ctx, TyTy::FnType *fntype);
 static tree
 assume_handler (Context *ctx, TyTy::FnType *fntype);
+static tree
+discriminant_value_handler (Context *ctx, TyTy::FnType *fntype);
 
 enum class Prefetch
 {
@@ -217,45 +214,45 @@ sorry_handler (Context *ctx, TyTy::FnType *fntype)
 
 static const std::map<std::string,
                      std::function<tree (Context *, TyTy::FnType *)>>
-  generic_intrinsics = {
-    {"offset", offset_handler},
-    {"size_of", sizeof_handler},
-    {"transmute", transmute_handler},
-    {"rotate_left", rotate_left_handler},
-    {"rotate_right", rotate_right_handler},
-    {"wrapping_add", wrapping_op_handler (PLUS_EXPR)},
-    {"wrapping_sub", wrapping_op_handler (MINUS_EXPR)},
-    {"wrapping_mul", wrapping_op_handler (MULT_EXPR)},
-    {"add_with_overflow", op_with_overflow (PLUS_EXPR)},
-    {"sub_with_overflow", op_with_overflow (MINUS_EXPR)},
-    {"mul_with_overflow", op_with_overflow (MULT_EXPR)},
-    {"copy", copy_handler (true)},
-    {"copy_nonoverlapping", copy_handler (false)},
-    {"prefetch_read_data", prefetch_read_data},
-    {"prefetch_write_data", prefetch_write_data},
-    {"atomic_store_seqcst", atomic_store_handler (__ATOMIC_SEQ_CST)},
-    {"atomic_store_release", atomic_store_handler (__ATOMIC_RELEASE)},
-    {"atomic_store_relaxed", atomic_store_handler (__ATOMIC_RELAXED)},
-    {"atomic_store_unordered", atomic_store_handler (__ATOMIC_RELAXED)},
-    {"atomic_load_seqcst", atomic_load_handler (__ATOMIC_SEQ_CST)},
-    {"atomic_load_acquire", atomic_load_handler (__ATOMIC_ACQUIRE)},
-    {"atomic_load_relaxed", atomic_load_handler (__ATOMIC_RELAXED)},
-    {"atomic_load_unordered", atomic_load_handler (__ATOMIC_RELAXED)},
-    {"unchecked_add", unchecked_op_handler (PLUS_EXPR)},
-    {"unchecked_sub", unchecked_op_handler (MINUS_EXPR)},
-    {"unchecked_mul", unchecked_op_handler (MULT_EXPR)},
-    {"unchecked_div", unchecked_op_handler (TRUNC_DIV_EXPR)},
-    {"unchecked_rem", unchecked_op_handler (TRUNC_MOD_EXPR)},
-    {"unchecked_shl", unchecked_op_handler (LSHIFT_EXPR)},
-    {"unchecked_shr", unchecked_op_handler (RSHIFT_EXPR)},
-    {"uninit", uninit_handler},
-    {"move_val_init", move_val_init_handler},
-    {"likely", expect_handler (true)},
-    {"unlikely", expect_handler (false)},
-    {"assume", assume_handler},
-    {"try", try_handler (false)},
-    {"catch_unwind", try_handler (true)},
-};
+  generic_intrinsics
+  = {{"offset", offset_handler},
+     {"size_of", sizeof_handler},
+     {"transmute", transmute_handler},
+     {"rotate_left", rotate_left_handler},
+     {"rotate_right", rotate_right_handler},
+     {"wrapping_add", wrapping_op_handler (PLUS_EXPR)},
+     {"wrapping_sub", wrapping_op_handler (MINUS_EXPR)},
+     {"wrapping_mul", wrapping_op_handler (MULT_EXPR)},
+     {"add_with_overflow", op_with_overflow (PLUS_EXPR)},
+     {"sub_with_overflow", op_with_overflow (MINUS_EXPR)},
+     {"mul_with_overflow", op_with_overflow (MULT_EXPR)},
+     {"copy", copy_handler (true)},
+     {"copy_nonoverlapping", copy_handler (false)},
+     {"prefetch_read_data", prefetch_read_data},
+     {"prefetch_write_data", prefetch_write_data},
+     {"atomic_store_seqcst", atomic_store_handler (__ATOMIC_SEQ_CST)},
+     {"atomic_store_release", atomic_store_handler (__ATOMIC_RELEASE)},
+     {"atomic_store_relaxed", atomic_store_handler (__ATOMIC_RELAXED)},
+     {"atomic_store_unordered", atomic_store_handler (__ATOMIC_RELAXED)},
+     {"atomic_load_seqcst", atomic_load_handler (__ATOMIC_SEQ_CST)},
+     {"atomic_load_acquire", atomic_load_handler (__ATOMIC_ACQUIRE)},
+     {"atomic_load_relaxed", atomic_load_handler (__ATOMIC_RELAXED)},
+     {"atomic_load_unordered", atomic_load_handler (__ATOMIC_RELAXED)},
+     {"unchecked_add", unchecked_op_handler (PLUS_EXPR)},
+     {"unchecked_sub", unchecked_op_handler (MINUS_EXPR)},
+     {"unchecked_mul", unchecked_op_handler (MULT_EXPR)},
+     {"unchecked_div", unchecked_op_handler (TRUNC_DIV_EXPR)},
+     {"unchecked_rem", unchecked_op_handler (TRUNC_MOD_EXPR)},
+     {"unchecked_shl", unchecked_op_handler (LSHIFT_EXPR)},
+     {"unchecked_shr", unchecked_op_handler (RSHIFT_EXPR)},
+     {"uninit", uninit_handler},
+     {"move_val_init", move_val_init_handler},
+     {"likely", expect_handler (true)},
+     {"unlikely", expect_handler (false)},
+     {"assume", assume_handler},
+     {"try", try_handler (false)},
+     {"catch_unwind", try_handler (true)},
+     {"discriminant_value", discriminant_value_handler}};
 
 Intrinsics::Intrinsics (Context *ctx) : ctx (ctx) {}
 
@@ -1375,5 +1372,69 @@ try_handler_inner (Context *ctx, TyTy::FnType *fntype, bool is_new_api)
   return fndecl;
 }
 
+static tree
+discriminant_value_handler (Context *ctx, TyTy::FnType *fntype)
+{
+  rust_assert (fntype->get_params ().size () == 1);
+  rust_assert (fntype->get_return_type ()->is<TyTy::PlaceholderType> ());
+  rust_assert (fntype->has_substitutions ());
+  rust_assert (fntype->get_num_type_params () == 1);
+  auto &mapping = fntype->get_substs ().at (0);
+  auto param_ty = mapping.get_param_ty ();
+  rust_assert (param_ty->can_resolve ());
+  auto resolved = param_ty->resolve ();
+  auto p = static_cast<TyTy::PlaceholderType *> (fntype->get_return_type ());
+
+  TyTy::BaseType *return_type = nullptr;
+  bool ok = ctx->get_tyctx ()->lookup_builtin ("isize", &return_type);
+  rust_assert (ok);
+
+  bool is_adt = resolved->is<TyTy::ADTType> ();
+  bool is_enum = false;
+  if (is_adt)
+    {
+      const auto &adt = *static_cast<TyTy::ADTType *> (resolved);
+      return_type = adt.get_repr_options ().repr;
+      rust_assert (return_type != nullptr);
+      is_enum = adt.is_enum ();
+    }
+
+  p->set_associated_type (return_type->get_ref ());
+
+  tree lookup = NULL_TREE;
+  if (check_for_cached_intrinsic (ctx, fntype, &lookup))
+    return lookup;
+
+  auto fndecl = compile_intrinsic_function (ctx, fntype);
+
+  std::vector<Bvariable *> param_vars;
+  compile_fn_params (ctx, fntype, fndecl, &param_vars);
+
+  if (!Backend::function_set_parameters (fndecl, param_vars))
+    return error_mark_node;
+
+  enter_intrinsic_block (ctx, fndecl);
+
+  // BUILTIN disriminant_value FN BODY BEGIN
+
+  tree result = integer_zero_node;
+  if (is_enum)
+    {
+      tree val = Backend::var_expression (param_vars[0], UNDEF_LOCATION);
+      tree deref = build_fold_indirect_ref_loc (UNKNOWN_LOCATION, val);
+      result = Backend::struct_field_expression (deref, 0, UNKNOWN_LOCATION);
+    }
+
+  auto return_statement
+    = Backend::return_statement (fndecl, result, BUILTINS_LOCATION);
+  ctx->add_statement (return_statement);
+
+  // BUILTIN disriminant_value FN BODY END
+
+  finalize_intrinsic_block (ctx, fndecl);
+
+  return fndecl;
+}
+
 } // namespace Compile
 } // namespace Rust
index 04af5abf8370439d112ecb8833f6ac218cdb0049..98323f63132ba999c63521bb18623a6949068238 100644 (file)
@@ -359,6 +359,7 @@ TraitItemReference::resolve_item (HIR::TraitItemType &type)
 {
   TyTy::BaseType *ty
     = new TyTy::PlaceholderType (type.get_name ().as_string (),
+                                type.get_mappings ().get_defid (),
                                 type.get_mappings ().get_hirid ());
   context->insert_type (type.get_mappings (), ty);
 }
index d8b4b23f71da18e3769680a03980f1c5a154d163..34f629c051939785df255743fd6d9f2dd83dd1eb 100644 (file)
@@ -292,6 +292,10 @@ TypeCheckBase::parse_repr_options (const AST::AttrVec &attrs, location_t locus)
   repr.pack = 0;
   repr.align = 0;
 
+  // FIXME handle repr types....
+  bool ok = context->lookup_builtin ("isize", &repr.repr);
+  rust_assert (ok);
+
   for (const auto &attr : attrs)
     {
       bool is_repr = attr.get_path ().as_string () == Values::Attributes::REPR;
index 4b78ef32c9b3cf46d5a4e8781cbf723e6c906385..5c38cb42b39b085fc9b4dce8931fa00fff9fb61d 100644 (file)
@@ -235,6 +235,52 @@ TypeCheckExpr::visit (HIR::CallExpr &expr)
     }
 
   infered = TyTy::TypeCheckCallExpr::go (function_tyty, expr, variant, context);
+
+  auto discriminant_type_lookup
+    = mappings.lookup_lang_item (LangItem::Kind::DISCRIMINANT_TYPE);
+  if (infered->is<TyTy::PlaceholderType> () && discriminant_type_lookup)
+    {
+      const auto &p = *static_cast<const TyTy::PlaceholderType *> (infered);
+      if (p.get_def_id () == discriminant_type_lookup.value ())
+       {
+         // this is a special case where this will actually return the repr of
+         // the enum. We dont currently support repr on enum yet to change the
+         // discriminant type but the default is always isize. We need to
+         // assert this is a generic function with one param
+         //
+         // fn<BookFormat> (v & T=BookFormat{Paperback) -> <placeholder:>
+         //
+         // note the default is isize
+
+         bool ok = context->lookup_builtin ("isize", &infered);
+         rust_assert (ok);
+
+         rust_assert (function_tyty->is<TyTy::FnType> ());
+         auto &fn = *static_cast<TyTy::FnType *> (function_tyty);
+         rust_assert (fn.has_substitutions ());
+         rust_assert (fn.get_num_type_params () == 1);
+         auto &mapping = fn.get_substs ().at (0);
+         auto param_ty = mapping.get_param_ty ();
+
+         if (!param_ty->can_resolve ())
+           {
+             // this could be a valid error need to test more weird cases and
+             // look at rustc
+             rust_internal_error_at (expr.get_locus (),
+                                     "something wrong computing return type");
+             return;
+           }
+
+         auto resolved = param_ty->resolve ();
+         bool is_adt = resolved->is<TyTy::ADTType> ();
+         if (is_adt)
+           {
+             const auto &adt = *static_cast<TyTy::ADTType *> (resolved);
+             infered = adt.get_repr_options ().repr;
+             rust_assert (infered != nullptr);
+           }
+       }
+    }
 }
 
 void
index e1bf735bb866365603d7b3de34e9ed02cecc93ff..16c664fb595b234afa4b30f4f46c280163db4612 100644 (file)
@@ -337,6 +337,11 @@ TypeCheckItem::visit (HIR::Enum &enum_decl)
   if (enum_decl.has_generics ())
     resolve_generic_params (enum_decl.get_generic_params (), substitutions);
 
+  // Process #[repr(X)] attribute, if any
+  const AST::AttrVec &attrs = enum_decl.get_outer_attrs ();
+  TyTy::ADTType::ReprOptions repr
+    = parse_repr_options (attrs, enum_decl.get_locus ());
+
   std::vector<TyTy::VariantDef *> variants;
   int64_t discriminant_value = 0;
   for (auto &variant : enum_decl.get_variants ())
@@ -375,7 +380,7 @@ TypeCheckItem::visit (HIR::Enum &enum_decl)
                         enum_decl.get_mappings ().get_hirid (),
                         enum_decl.get_identifier ().as_string (), ident,
                         TyTy::ADTType::ADTKind::ENUM, std::move (variants),
-                        std::move (substitutions));
+                        std::move (substitutions), repr);
 
   context->insert_type (enum_decl.get_mappings (), type);
   infered = type;
index 93913e89cb782e391404c8194a16eb38ac8b55c7..6afa7cf3af9fe9985f06596a81f80daf7ec7c206 100644 (file)
@@ -218,14 +218,6 @@ TypeCheckType::visit (HIR::QualifiedPathInType &path)
   if (trait_ref->is_error ())
     return;
 
-  // does this type actually implement this type-bound?
-  if (!TypeBoundsProbe::is_bound_satisfied_for_type (root, trait_ref))
-    {
-      rust_error_at (qual_path_type.get_locus (),
-                    "root does not satisfy specified trait-bound");
-      return;
-    }
-
   // get the predicate for the bound
   auto specified_bound = get_predicate_from_bound (qual_path_type.get_trait (),
                                                   qual_path_type.get_type ());
index 02d91b1f1950c5df1193cdef388ad2cca5627aa8..8f388120a41c7a67bb4d7ab3f5615294336ff901 100644 (file)
@@ -3483,20 +3483,20 @@ NeverType::clone () const
 
 // placeholder type
 
-PlaceholderType::PlaceholderType (std::string symbol, HirId ref,
+PlaceholderType::PlaceholderType (std::string symbol, DefId id, HirId ref,
                                  std::set<HirId> refs)
   : BaseType (ref, ref, KIND,
              {Resolver::CanonicalPath::create_empty (), BUILTINS_LOCATION},
              refs),
-    symbol (symbol)
+    symbol (symbol), defId (id)
 {}
 
-PlaceholderType::PlaceholderType (std::string symbol, HirId ref, HirId ty_ref,
-                                 std::set<HirId> refs)
+PlaceholderType::PlaceholderType (std::string symbol, DefId id, HirId ref,
+                                 HirId ty_ref, std::set<HirId> refs)
   : BaseType (ref, ty_ref, KIND,
              {Resolver::CanonicalPath::create_empty (), BUILTINS_LOCATION},
              refs),
-    symbol (symbol)
+    symbol (symbol), defId (id)
 {}
 
 std::string
@@ -3540,8 +3540,8 @@ PlaceholderType::can_eq (const BaseType *other, bool emit_errors) const
 BaseType *
 PlaceholderType::clone () const
 {
-  return new PlaceholderType (get_symbol (), get_ref (), get_ty_ref (),
-                             get_combined_refs ());
+  return new PlaceholderType (get_symbol (), get_def_id (), get_ref (),
+                             get_ty_ref (), get_combined_refs ());
 }
 
 void
@@ -3602,6 +3602,12 @@ PlaceholderType::is_equal (const BaseType &other) const
   return get_symbol ().compare (other2.get_symbol ()) == 0;
 }
 
+DefId
+PlaceholderType::get_def_id () const
+{
+  return defId;
+}
+
 // Projection type
 
 ProjectionType::ProjectionType (
index a41837e35afa069b0733ba582793c4dd8082a7d3..93c4a15a4607aa057fffef72cc629270f44e6f69 100644 (file)
@@ -648,6 +648,7 @@ public:
     // parsing the #[repr] attribute.
     unsigned char align = 0;
     unsigned char pack = 0;
+    BaseType *repr = nullptr;
   };
 
   ADTType (HirId ref, std::string identifier, RustIdent ident, ADTKind adt_kind,
@@ -1530,9 +1531,9 @@ class PlaceholderType : public BaseType
 public:
   static constexpr auto KIND = TypeKind::PLACEHOLDER;
 
-  PlaceholderType (std::string symbol, HirId ref,
+  PlaceholderType (std::string symbol, DefId id, HirId ref,
                   std::set<HirId> refs = std::set<HirId> ());
-  PlaceholderType (std::string symbol, HirId ref, HirId ty_ref,
+  PlaceholderType (std::string symbol, DefId id, HirId ref, HirId ty_ref,
                   std::set<HirId> refs = std::set<HirId> ());
 
   void accept_vis (TyVisitor &vis) override;
@@ -1558,8 +1559,11 @@ public:
 
   bool is_equal (const BaseType &other) const override;
 
+  DefId get_def_id () const;
+
 private:
   std::string symbol;
+  DefId defId;
 };
 
 class ProjectionType : public BaseType, public SubstitutionRef
index 92a76613b61d325176aca9e4d2d5a787dbf6bf45..a76cc7ff6519c4542259d742cf8cd486a0192081 100644 (file)
@@ -115,6 +115,9 @@ const BiMap<std::string, LangItem::Kind> Rust::LangItem::lang_items = {{
 
   {"structural_peq", Kind::STRUCTURAL_PEQ},
   {"structural_teq", Kind::STRUCTURAL_TEQ},
+
+  {"discriminant_kind", Kind::DISCRIMINANT_KIND},
+  {"discriminant_type", Kind::DISCRIMINANT_TYPE},
 }};
 
 tl::optional<LangItem::Kind>
index 29b972702bd16b4eac8fe941e7c9ebc3760404e5..8f3af3615bb2a50828e0fc8604c9e4943f0cac48 100644 (file)
@@ -147,6 +147,9 @@ public:
 
     STRUCTURAL_PEQ,
     STRUCTURAL_TEQ,
+
+    DISCRIMINANT_TYPE,
+    DISCRIMINANT_KIND,
   };
 
   static const BiMap<std::string, Kind> lang_items;
diff --git a/gcc/testsuite/rust/execute/torture/enum_intrinsics1.rs b/gcc/testsuite/rust/execute/torture/enum_intrinsics1.rs
new file mode 100644 (file)
index 0000000..c30bbd3
--- /dev/null
@@ -0,0 +1,48 @@
+/* { dg-output "0\r*\n2\r*\n" } */
+#![feature(intrinsics)]
+
+#[lang = "sized"]
+pub trait Sized {}
+
+enum BookFormat {
+    Paperback,
+    Hardback,
+    Ebook,
+}
+
+extern "C" {
+    fn printf(s: *const i8, ...);
+}
+
+mod core {
+    mod intrinsics {
+        #[lang = "discriminant_kind"]
+        pub trait DiscriminantKind {
+            #[lang = "discriminant_type"]
+            type Discriminant;
+        }
+
+        extern "rust-intrinsic" {
+            pub fn discriminant_value<T>(v: &T) -> <T as DiscriminantKind>::Discriminant;
+        }
+    }
+}
+
+pub fn main() -> i32 {
+    let a = BookFormat::Paperback;
+    let b = BookFormat::Ebook;
+
+    unsafe {
+        let val1: isize = core::intrinsics::discriminant_value(&a);
+        let val2 = core::intrinsics::discriminant_value(&b);
+
+        let a = "%i\n";
+        let b = a as *const str;
+        let c = b as *const i8;
+
+        printf(c, val1 as i32);
+        printf(c, val2 as i32);
+    }
+
+    0
+}