https://gcc.gnu.org/g:db785937718f04b95d37f37ade557080d69a5e2a
commit r17-5711-gdb785937718f04b95d37f37ade557080d69a5e2a Author: Arthur Cohen <[email protected]> Date: Wed Oct 21 12:22:46 2026 +0200 gccrs: intrinsics: Add assert_inhabited<T> 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. Diff: --- gcc/rust/backend/rust-compile-intrinsic.cc | 1 + gcc/rust/backend/rust-intrinsic-handlers.cc | 82 ++++++++++++++++++++++ gcc/rust/backend/rust-intrinsic-handlers.h | 2 + .../typecheck/rust-hir-type-check-intrinsic.cc | 3 + gcc/rust/util/rust-intrinsic-values.h | 2 + gcc/testsuite/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(+) diff --git a/gcc/rust/backend/rust-compile-intrinsic.cc b/gcc/rust/backend/rust-compile-intrinsic.cc index 312d0f778949..7b56056ae65e 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 d9b56b7e341e..82bb5b4d30fe 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 8bccd65b0716..7ab3e8a701fa 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 ae1bc3e1dcff..258f85faa0da 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 42af67988a52..5f498d16b215 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 000000000000..5c541589aa19 --- /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 000000000000..e1a58a3a8791 --- /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 000000000000..38b4972bced2 --- /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 +}
