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.
---
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/0c159eabf530dace86d5b24fc7abd3f7b63b7527
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/5007
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 312d0f778..7b56056ae 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 d9b56b7e3..82bb5b4d3 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 8bccd65b0..7ab3e8a70 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 ae1bc3e1d..258f85faa 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 42af67988..5f498d16b 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 000000000..5c541589a
--- /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 000000000..e1a58a3a8
--- /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 000000000..38b4972bc
--- /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
+}
base-commit: 783bc1df4c7b607614ec8267bca9fcde7b77fcda
--
2.55.0