From: Arthur Cohen <[email protected]>

gcc/rust/ChangeLog:

        * backend/rust-compile-intrinsic.cc: Add handler for assert_inhabited.
        * backend/rust-intrinsic-handlers.cc (lang_item_call): New function for 
calling functions
        which are lang items.
        (build_rust_string): Helper for creating Rust &str from the handlers.
        (assert_inhabited): New handler.
        * backend/rust-intrinsic-handlers.h (assert_inhabited): Declare it.
        * typecheck/rust-hir-type-check-intrinsic.cc: Add typecheck info for 
assert_inhabited.
        * util/rust-intrinsic-values.h: Add assert_inhabited.

gcc/testsuite/ChangeLog:

        * rust/compile/assert_inhabited.rs: New test.
        * rust/execute/torture/assert_inhabited1.rs: New test.
        * rust/execute/torture/assert_inhabited2.rs: New test.

Tested on x86_64-pc-linux-gnu.

---
 gcc/rust/backend/rust-compile-intrinsic.cc    |  1 +
 gcc/rust/backend/rust-intrinsic-handlers.cc   | 82 +++++++++++++++++++
 gcc/rust/backend/rust-intrinsic-handlers.h    |  2 +
 .../rust-hir-type-check-intrinsic.cc          |  3 +
 gcc/rust/util/rust-intrinsic-values.h         |  2 +
 .../rust/compile/assert_inhabited.rs          | 30 +++++++
 .../rust/execute/torture/assert_inhabited1.rs | 33 ++++++++
 .../rust/execute/torture/assert_inhabited2.rs | 30 +++++++
 8 files changed, 183 insertions(+)
 create mode 100644 gcc/testsuite/rust/compile/assert_inhabited.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/assert_inhabited1.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/assert_inhabited2.rs

diff --git a/gcc/rust/backend/rust-compile-intrinsic.cc 
b/gcc/rust/backend/rust-compile-intrinsic.cc
index 312d0f77894..7b56056ae65 100644
--- a/gcc/rust/backend/rust-compile-intrinsic.cc
+++ b/gcc/rust/backend/rust-compile-intrinsic.cc
@@ -92,6 +92,7 @@ static const std::map<std::string, Intrinsic::CompileFn> 
intrinsics = {
    handlers::fop_fast (ArithmeticOrLogicalOperator::MODULUS)},
   {IValue::FLOAT_TO_INT_UNCHECKED, handlers::float_to_int_unchecked},
   {IValue::EXACT_DIV, handlers::exact_div_handler},
+  {IValue::ASSERT_INHABITED, handlers::assert_inhabited},
 };
 
 Intrinsics::Intrinsics (Context *ctx) : ctx (ctx) {}
diff --git a/gcc/rust/backend/rust-intrinsic-handlers.cc 
b/gcc/rust/backend/rust-intrinsic-handlers.cc
index d9b56b7e341..82bb5b4d30f 100644
--- a/gcc/rust/backend/rust-intrinsic-handlers.cc
+++ b/gcc/rust/backend/rust-intrinsic-handlers.cc
@@ -20,6 +20,7 @@
 #include "optional.h"
 #include "rust-backend.h"
 #include "rust-compile-context.h"
+#include "rust-compile-item.h"
 #include "rust-compile-type.h"
 #include "rust-compile-fnparam.h"
 #include "rust-builtins.h"
@@ -33,6 +34,8 @@
 #include "rust-gcc.h"
 #include "fold-const.h"
 #include "rust-constexpr.h"
+#include "rust-hir-item.h"
+#include "tree.h"
 
 // declaration taken from "stringpool.h"
 // the get_identifier macro causes compilation issues
@@ -41,6 +44,26 @@ extern tree get_identifier (const char *);
 namespace Rust {
 namespace Compile {
 
+static tree
+lang_item_call (IntrinsicCtx &ctx, LangItem::Kind lang_item,
+               const std::vector<tree> &args)
+{
+  auto defid = ctx.get_mappings ().lookup_lang_item (lang_item);
+  rust_assert (defid);
+
+  auto lang_item_def = ctx.get_mappings ().lookup_defid (*defid);
+  rust_assert (lang_item_def);
+
+  // Let's make sure we're not calling a lang item that's a type...
+  rust_assert (lang_item_def.value ()->get_item_kind ()
+              == HIR::Item::ItemKind::Function);
+
+  // Compile the lang item function
+  auto fn = CompileItem::compile (*lang_item_def, &ctx.ctx);
+
+  return Backend::call_expression (fn, args, nullptr, ctx.loc);
+}
+
 tree
 Intrinsic::compile (Context *ctx, TyTy::FnType *fntype, location_t loc)
 {
@@ -1775,6 +1798,65 @@ float_to_int_unchecked (IntrinsicCtx &ctx)
   ctx.add_statement (return_statement);
 }
 
+static tree
+build_rust_string (IntrinsicCtx &ctx, const std::string &content)
+{
+  TyTy::BaseType *str = nullptr;
+  TyTy::BaseType *usize = nullptr;
+  rust_assert (ctx.get_tyctx ()->lookup_builtin ("str", &str));
+  rust_assert (ctx.get_tyctx ()->lookup_builtin ("usize", &usize));
+
+  tree meta_pointer = TyTyResolveCompile::compile (ctx.inner (), str);
+  tree usize_ty = TyTyResolveCompile::compile (ctx.inner (), usize);
+
+  auto base = Backend::string_constant_expression (content);
+  tree data = build_fold_addr_expr (base);
+
+  tree size = build_int_cstu (usize_ty, content.size ());
+
+  return Backend::constructor_expression (meta_pointer, false, {data, size}, 
-1,
+                                         ctx.loc);
+}
+
+void
+assert_inhabited (IntrinsicCtx &ctx)
+{
+  /**
+   * This function checks whether a type is uninhabited *statically* - if the
+   * given generic is never or an empty enum, then panic at runtime, otherwise
+   * do nothing
+   */
+
+  auto t_param = ctx.fntype.get_substs ().at (0);
+  auto t_ty = t_param.get_param_ty ();
+  auto t = t_ty->resolve ();
+  auto t_name = t->get_name ();
+
+  auto inhabited = true;
+
+  if (auto adt = t->try_as<TyTy::ADTType> ())
+    if (adt->is_enum () && adt->number_of_variants () == 0)
+      inhabited = false;
+
+  if (t->is<TyTy::NeverType> ())
+    inhabited = false;
+
+  if (!inhabited)
+    {
+      auto fmt = "attempted to instantiate uninhabited type `" + t_name + "`";
+      auto arg = build_rust_string (ctx, fmt);
+
+      auto panic_call = lang_item_call (ctx, LangItem::Kind::PANIC, {arg});
+
+      ctx.add_statement (panic_call);
+    }
+
+  // buh :(
+  TREE_READONLY (ctx.fn) = 0;
+  TREE_USED (ctx.fn) = 1;
+  TREE_SIDE_EFFECTS (ctx.fn) = 1;
+}
+
 } // namespace handlers
 } // namespace Compile
 } // namespace Rust
diff --git a/gcc/rust/backend/rust-intrinsic-handlers.h 
b/gcc/rust/backend/rust-intrinsic-handlers.h
index 8bccd65b071..7ab3e8a701f 100644
--- a/gcc/rust/backend/rust-intrinsic-handlers.h
+++ b/gcc/rust/backend/rust-intrinsic-handlers.h
@@ -195,6 +195,8 @@ Intrinsic::CompileFn try_handler (bool is_new_api);
  */
 Intrinsic::CompileFn fop_fast (ArithmeticOrLogicalOperator op);
 
+void assert_inhabited (IntrinsicCtx &ctx);
+
 } // namespace handlers
 
 } // namespace Compile
diff --git a/gcc/rust/typecheck/rust-hir-type-check-intrinsic.cc 
b/gcc/rust/typecheck/rust-hir-type-check-intrinsic.cc
index ae1bc3e1dcf..258f85faa0d 100644
--- a/gcc/rust/typecheck/rust-hir-type-check-intrinsic.cc
+++ b/gcc/rust/typecheck/rust-hir-type-check-intrinsic.cc
@@ -311,8 +311,11 @@ const std::unordered_map<std::string, IntrinsicRules>
     // fn write_bytes<T>(dst: *mut T, val: u8, count: usize)
     {IValue::WRITE_BYTES,
      {1, {IRT::MutPtrFirstGeneric, IRT::U8, IRT::Usize}, IRT::Unit}},
+
     // pub fn assert_zero_valid<T>();
     {IValue::ASSERT_ZERO_VALID, {1, {}, IRT::Unit}},
+    // fn assert_inhabited<T>();
+    {IValue::ASSERT_INHABITED, {1, {}, IRT::Unit}},
 
     // pub fn fadd_fast<T>(a: T, b: T) -> T
     // same for fsub_fast, fmul_fast, fdiv_fast and frem_fast
diff --git a/gcc/rust/util/rust-intrinsic-values.h 
b/gcc/rust/util/rust-intrinsic-values.h
index 42af67988a5..5f498d16b21 100644
--- a/gcc/rust/util/rust-intrinsic-values.h
+++ b/gcc/rust/util/rust-intrinsic-values.h
@@ -174,7 +174,9 @@ public:
   static constexpr auto &BLACK_BOX = "black_box";
   static constexpr auto &ARITH_OFFSET = "arith_offset";
   static constexpr auto &WRITE_BYTES = "write_bytes";
+
   static constexpr auto &ASSERT_ZERO_VALID = "assert_zero_valid";
+  static constexpr auto &ASSERT_INHABITED = "assert_inhabited";
 
   static constexpr auto &FADD_FAST = "fadd_fast";
   static constexpr auto &FSUB_FAST = "fsub_fast";
diff --git a/gcc/testsuite/rust/compile/assert_inhabited.rs 
b/gcc/testsuite/rust/compile/assert_inhabited.rs
new file mode 100644
index 00000000000..5c541589aa1
--- /dev/null
+++ b/gcc/testsuite/rust/compile/assert_inhabited.rs
@@ -0,0 +1,30 @@
+#![feature(no_core)]
+#![feature(intrinsics)]
+#![feature(lang_items)]
+#![no_core]
+
+#[lang = "sized"]
+trait Sized {}
+
+#[lang = "panic"]
+pub fn panic(s: &'static str) -> ! {
+    unsafe {
+        puts(s as *const str as *const i8);
+    }
+}
+
+extern "C" {
+    fn puts(s: *const i8);
+}
+
+extern "rust-intrinsic" {
+    fn assert_inhabited<T>();
+}
+
+enum Nothing {}
+
+fn main() {
+    unsafe { assert_inhabited::<Nothing>() };
+    unsafe { assert_inhabited::<!>() };
+    unsafe { assert_inhabited::<i32>() };
+}
diff --git a/gcc/testsuite/rust/execute/torture/assert_inhabited1.rs 
b/gcc/testsuite/rust/execute/torture/assert_inhabited1.rs
new file mode 100644
index 00000000000..e1a58a3a879
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/assert_inhabited1.rs
@@ -0,0 +1,33 @@
+#![feature(no_core)]
+#![feature(intrinsics)]
+#![feature(lang_items)]
+#![no_core]
+
+// { dg-output "attempted to instantiate uninhabited type `!`\r*\nattempted to 
instantiate uninhabited type `Nothing`\r*\n" }
+
+#[lang = "sized"]
+trait Sized {}
+
+#[lang = "panic"]
+pub fn panic(s: &'static str) -> ! {
+    unsafe {
+        puts(s as *const str as *const i8);
+    }
+}
+
+extern "C" {
+    fn puts(s: *const i8);
+}
+
+extern "rust-intrinsic" {
+    fn assert_inhabited<T>();
+}
+
+enum Nothing {}
+
+fn main() -> i32 {
+    unsafe { assert_inhabited::<!>() };
+    unsafe { assert_inhabited::<Nothing>() };
+
+    0
+}
diff --git a/gcc/testsuite/rust/execute/torture/assert_inhabited2.rs 
b/gcc/testsuite/rust/execute/torture/assert_inhabited2.rs
new file mode 100644
index 00000000000..38b4972bced
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/assert_inhabited2.rs
@@ -0,0 +1,30 @@
+#![feature(no_core)]
+#![feature(intrinsics)]
+#![feature(lang_items)]
+#![no_core]
+
+#[lang = "sized"]
+trait Sized {}
+
+#[lang = "panic"]
+pub fn panic(s: &'static str) -> ! {
+    unsafe {
+        puts(s as *const str as *const i8);
+    }
+}
+
+extern "C" {
+    fn puts(s: *const i8);
+}
+
+extern "rust-intrinsic" {
+    fn assert_inhabited<T>();
+}
+
+// no output, i32 is inhabited
+
+fn main() -> i32 {
+    unsafe { assert_inhabited::<i32>() };
+
+    0
+}
-- 
2.55.0

Reply via email to