mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-25 08:13:41 +00:00
Auto merge of #132167 - Zalathar:llvm-wrappers, r=jieyouxu
Replace some LLVMRust wrappers with calls to the LLVM C API This PR removes the LLVMRust wrapper functions for getting/setting linkage and visibility, and replaces them with direct calls to the corresponding functions in LLVM's C API. To make this convenient and sound, two pieces of supporting code have also been added: - A simple proc-macro that derives `TryFrom<u32>` for fieldless enums - A wrapper type for C enum values returned by LLVM functions, to ensure soundness if LLVM returns an enum value we don't know about In a few places, the use of safe wrapper functions means that an `unsafe` block is no longer needed, so the affected code has changed its indentation level.
This commit is contained in:
commit
be33e4f3d6
@ -77,20 +77,14 @@ pub(crate) unsafe fn codegen(
|
||||
// __rust_alloc_error_handler_should_panic
|
||||
let name = OomStrategy::SYMBOL;
|
||||
let ll_g = llvm::LLVMRustGetOrInsertGlobal(llmod, name.as_ptr().cast(), name.len(), i8);
|
||||
llvm::LLVMRustSetVisibility(
|
||||
ll_g,
|
||||
llvm::Visibility::from_generic(tcx.sess.default_visibility()),
|
||||
);
|
||||
llvm::set_visibility(ll_g, llvm::Visibility::from_generic(tcx.sess.default_visibility()));
|
||||
let val = tcx.sess.opts.unstable_opts.oom.should_panic();
|
||||
let llval = llvm::LLVMConstInt(i8, val as u64, False);
|
||||
llvm::LLVMSetInitializer(ll_g, llval);
|
||||
|
||||
let name = NO_ALLOC_SHIM_IS_UNSTABLE;
|
||||
let ll_g = llvm::LLVMRustGetOrInsertGlobal(llmod, name.as_ptr().cast(), name.len(), i8);
|
||||
llvm::LLVMRustSetVisibility(
|
||||
ll_g,
|
||||
llvm::Visibility::from_generic(tcx.sess.default_visibility()),
|
||||
);
|
||||
llvm::set_visibility(ll_g, llvm::Visibility::from_generic(tcx.sess.default_visibility()));
|
||||
let llval = llvm::LLVMConstInt(i8, 0, False);
|
||||
llvm::LLVMSetInitializer(ll_g, llval);
|
||||
}
|
||||
@ -134,10 +128,7 @@ fn create_wrapper_function(
|
||||
None
|
||||
};
|
||||
|
||||
llvm::LLVMRustSetVisibility(
|
||||
llfn,
|
||||
llvm::Visibility::from_generic(tcx.sess.default_visibility()),
|
||||
);
|
||||
llvm::set_visibility(llfn, llvm::Visibility::from_generic(tcx.sess.default_visibility()));
|
||||
|
||||
if tcx.sess.must_emit_unwind_tables() {
|
||||
let uwtable =
|
||||
@ -151,7 +142,7 @@ fn create_wrapper_function(
|
||||
// -> ! DIFlagNoReturn
|
||||
attributes::apply_to_llfn(callee, llvm::AttributePlace::Function, &[no_return]);
|
||||
}
|
||||
llvm::LLVMRustSetVisibility(callee, llvm::Visibility::Hidden);
|
||||
llvm::set_visibility(callee, llvm::Visibility::Hidden);
|
||||
|
||||
let llbb = llvm::LLVMAppendBasicBlockInContext(llcx, llfn, c"entry".as_ptr());
|
||||
|
||||
|
@ -1043,7 +1043,7 @@ unsafe fn embed_bitcode(
|
||||
|
||||
let section = bitcode_section_name(cgcx);
|
||||
llvm::LLVMSetSection(llglobal, section.as_ptr().cast());
|
||||
llvm::LLVMRustSetLinkage(llglobal, llvm::Linkage::PrivateLinkage);
|
||||
llvm::set_linkage(llglobal, llvm::Linkage::PrivateLinkage);
|
||||
llvm::LLVMSetGlobalConstant(llglobal, llvm::True);
|
||||
|
||||
let llconst = common::bytes_in_context(llcx, cmdline.as_bytes());
|
||||
@ -1061,7 +1061,7 @@ unsafe fn embed_bitcode(
|
||||
c".llvmcmd"
|
||||
};
|
||||
llvm::LLVMSetSection(llglobal, section.as_ptr());
|
||||
llvm::LLVMRustSetLinkage(llglobal, llvm::Linkage::PrivateLinkage);
|
||||
llvm::set_linkage(llglobal, llvm::Linkage::PrivateLinkage);
|
||||
} else {
|
||||
// We need custom section flags, so emit module-level inline assembly.
|
||||
let section_flags = if cgcx.is_pe_coff { "n" } else { "e" };
|
||||
@ -1096,7 +1096,7 @@ fn create_msvc_imps(
|
||||
let ptr_ty = Type::ptr_llcx(llcx);
|
||||
let globals = base::iter_globals(llmod)
|
||||
.filter(|&val| {
|
||||
llvm::LLVMRustGetLinkage(val) == llvm::Linkage::ExternalLinkage
|
||||
llvm::get_linkage(val) == llvm::Linkage::ExternalLinkage
|
||||
&& llvm::LLVMIsDeclaration(val) == 0
|
||||
})
|
||||
.filter_map(|val| {
|
||||
@ -1115,7 +1115,7 @@ fn create_msvc_imps(
|
||||
for (imp_name, val) in globals {
|
||||
let imp = llvm::LLVMAddGlobal(llmod, ptr_ty, imp_name.as_ptr());
|
||||
llvm::LLVMSetInitializer(imp, val);
|
||||
llvm::LLVMRustSetLinkage(imp, llvm::Linkage::ExternalLinkage);
|
||||
llvm::set_linkage(imp, llvm::Linkage::ExternalLinkage);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -95,9 +95,9 @@ pub(crate) fn get_fn<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, instance: Instance<'t
|
||||
// whether we are sharing generics or not. The important thing here is
|
||||
// that the visibility we apply to the declaration is the same one that
|
||||
// has been applied to the definition (wherever that definition may be).
|
||||
unsafe {
|
||||
llvm::LLVMRustSetLinkage(llfn, llvm::Linkage::ExternalLinkage);
|
||||
|
||||
llvm::set_linkage(llfn, llvm::Linkage::ExternalLinkage);
|
||||
unsafe {
|
||||
let is_generic = instance.args.non_erasable_generics().next().is_some();
|
||||
|
||||
let is_hidden = if is_generic {
|
||||
@ -135,7 +135,7 @@ pub(crate) fn get_fn<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, instance: Instance<'t
|
||||
|| !cx.tcx.is_reachable_non_generic(instance_def_id))
|
||||
};
|
||||
if is_hidden {
|
||||
llvm::LLVMRustSetVisibility(llfn, llvm::Visibility::Hidden);
|
||||
llvm::set_visibility(llfn, llvm::Visibility::Hidden);
|
||||
}
|
||||
|
||||
// MinGW: For backward compatibility we rely on the linker to decide whether it
|
||||
|
@ -219,8 +219,8 @@ impl<'ll, 'tcx> ConstCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> {
|
||||
llvm::LLVMSetInitializer(g, sc);
|
||||
llvm::LLVMSetGlobalConstant(g, True);
|
||||
llvm::LLVMSetUnnamedAddress(g, llvm::UnnamedAddr::Global);
|
||||
llvm::LLVMRustSetLinkage(g, llvm::Linkage::InternalLinkage);
|
||||
}
|
||||
llvm::set_linkage(g, llvm::Linkage::InternalLinkage);
|
||||
(s.to_owned(), g)
|
||||
})
|
||||
.1;
|
||||
|
@ -172,29 +172,27 @@ fn check_and_apply_linkage<'ll, 'tcx>(
|
||||
if let Some(linkage) = attrs.import_linkage {
|
||||
debug!("get_static: sym={} linkage={:?}", sym, linkage);
|
||||
|
||||
unsafe {
|
||||
// Declare a symbol `foo` with the desired linkage.
|
||||
let g1 = cx.declare_global(sym, cx.type_i8());
|
||||
llvm::LLVMRustSetLinkage(g1, base::linkage_to_llvm(linkage));
|
||||
// Declare a symbol `foo` with the desired linkage.
|
||||
let g1 = cx.declare_global(sym, cx.type_i8());
|
||||
llvm::set_linkage(g1, base::linkage_to_llvm(linkage));
|
||||
|
||||
// Declare an internal global `extern_with_linkage_foo` which
|
||||
// is initialized with the address of `foo`. If `foo` is
|
||||
// discarded during linking (for example, if `foo` has weak
|
||||
// linkage and there are no definitions), then
|
||||
// `extern_with_linkage_foo` will instead be initialized to
|
||||
// zero.
|
||||
let mut real_name = "_rust_extern_with_linkage_".to_string();
|
||||
real_name.push_str(sym);
|
||||
let g2 = cx.define_global(&real_name, llty).unwrap_or_else(|| {
|
||||
cx.sess().dcx().emit_fatal(SymbolAlreadyDefined {
|
||||
span: cx.tcx.def_span(def_id),
|
||||
symbol_name: sym,
|
||||
})
|
||||
});
|
||||
llvm::LLVMRustSetLinkage(g2, llvm::Linkage::InternalLinkage);
|
||||
llvm::LLVMSetInitializer(g2, g1);
|
||||
g2
|
||||
}
|
||||
// Declare an internal global `extern_with_linkage_foo` which
|
||||
// is initialized with the address of `foo`. If `foo` is
|
||||
// discarded during linking (for example, if `foo` has weak
|
||||
// linkage and there are no definitions), then
|
||||
// `extern_with_linkage_foo` will instead be initialized to
|
||||
// zero.
|
||||
let mut real_name = "_rust_extern_with_linkage_".to_string();
|
||||
real_name.push_str(sym);
|
||||
let g2 = cx.define_global(&real_name, llty).unwrap_or_else(|| {
|
||||
cx.sess().dcx().emit_fatal(SymbolAlreadyDefined {
|
||||
span: cx.tcx.def_span(def_id),
|
||||
symbol_name: sym,
|
||||
})
|
||||
});
|
||||
llvm::set_linkage(g2, llvm::Linkage::InternalLinkage);
|
||||
unsafe { llvm::LLVMSetInitializer(g2, g1) };
|
||||
g2
|
||||
} else if cx.tcx.sess.target.arch == "x86"
|
||||
&& let Some(dllimport) = crate::common::get_dllimport(cx.tcx, def_id, sym)
|
||||
{
|
||||
@ -224,23 +222,21 @@ impl<'ll> CodegenCx<'ll, '_> {
|
||||
align: Align,
|
||||
kind: Option<&str>,
|
||||
) -> &'ll Value {
|
||||
unsafe {
|
||||
let gv = match kind {
|
||||
Some(kind) if !self.tcx.sess.fewer_names() => {
|
||||
let name = self.generate_local_symbol_name(kind);
|
||||
let gv = self.define_global(&name, self.val_ty(cv)).unwrap_or_else(|| {
|
||||
bug!("symbol `{}` is already defined", name);
|
||||
});
|
||||
llvm::LLVMRustSetLinkage(gv, llvm::Linkage::PrivateLinkage);
|
||||
gv
|
||||
}
|
||||
_ => self.define_private_global(self.val_ty(cv)),
|
||||
};
|
||||
llvm::LLVMSetInitializer(gv, cv);
|
||||
set_global_alignment(self, gv, align);
|
||||
llvm::SetUnnamedAddress(gv, llvm::UnnamedAddr::Global);
|
||||
gv
|
||||
}
|
||||
let gv = match kind {
|
||||
Some(kind) if !self.tcx.sess.fewer_names() => {
|
||||
let name = self.generate_local_symbol_name(kind);
|
||||
let gv = self.define_global(&name, self.val_ty(cv)).unwrap_or_else(|| {
|
||||
bug!("symbol `{}` is already defined", name);
|
||||
});
|
||||
llvm::set_linkage(gv, llvm::Linkage::PrivateLinkage);
|
||||
gv
|
||||
}
|
||||
_ => self.define_private_global(self.val_ty(cv)),
|
||||
};
|
||||
unsafe { llvm::LLVMSetInitializer(gv, cv) };
|
||||
set_global_alignment(self, gv, align);
|
||||
llvm::SetUnnamedAddress(gv, llvm::UnnamedAddr::Global);
|
||||
gv
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
@ -292,9 +288,7 @@ impl<'ll> CodegenCx<'ll, '_> {
|
||||
let g = self.declare_global(sym, llty);
|
||||
|
||||
if !self.tcx.is_reachable_non_generic(def_id) {
|
||||
unsafe {
|
||||
llvm::LLVMRustSetVisibility(g, llvm::Visibility::Hidden);
|
||||
}
|
||||
llvm::set_visibility(g, llvm::Visibility::Hidden);
|
||||
}
|
||||
|
||||
g
|
||||
@ -312,7 +306,7 @@ impl<'ll> CodegenCx<'ll, '_> {
|
||||
llvm::set_thread_local_mode(g, self.tls_model);
|
||||
}
|
||||
|
||||
let dso_local = unsafe { self.should_assume_dso_local(g, true) };
|
||||
let dso_local = self.should_assume_dso_local(g, true);
|
||||
if dso_local {
|
||||
unsafe {
|
||||
llvm::LLVMRustSetDSOLocal(g, true);
|
||||
@ -401,8 +395,8 @@ impl<'ll> CodegenCx<'ll, '_> {
|
||||
let name = llvm::get_value_name(g).to_vec();
|
||||
llvm::set_value_name(g, b"");
|
||||
|
||||
let linkage = llvm::LLVMRustGetLinkage(g);
|
||||
let visibility = llvm::LLVMRustGetVisibility(g);
|
||||
let linkage = llvm::get_linkage(g);
|
||||
let visibility = llvm::get_visibility(g);
|
||||
|
||||
let new_g = llvm::LLVMRustGetOrInsertGlobal(
|
||||
self.llmod,
|
||||
@ -411,8 +405,8 @@ impl<'ll> CodegenCx<'ll, '_> {
|
||||
val_llty,
|
||||
);
|
||||
|
||||
llvm::LLVMRustSetLinkage(new_g, linkage);
|
||||
llvm::LLVMRustSetVisibility(new_g, visibility);
|
||||
llvm::set_linkage(new_g, linkage);
|
||||
llvm::set_visibility(new_g, visibility);
|
||||
|
||||
// The old global has had its name removed but is returned by
|
||||
// get_static since it is in the instance cache. Provide an
|
||||
|
@ -605,7 +605,7 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
|
||||
unsafe {
|
||||
let g = llvm::LLVMAddGlobal(self.llmod, self.val_ty(array), name.as_ptr());
|
||||
llvm::LLVMSetInitializer(g, array);
|
||||
llvm::LLVMRustSetLinkage(g, llvm::Linkage::AppendingLinkage);
|
||||
llvm::set_linkage(g, llvm::Linkage::AppendingLinkage);
|
||||
llvm::LLVMSetSection(g, c"llvm.metadata".as_ptr());
|
||||
}
|
||||
}
|
||||
|
@ -76,7 +76,7 @@ pub(crate) fn get_or_insert_gdb_debug_scripts_section_global<'ll>(
|
||||
llvm::LLVMSetInitializer(section_var, cx.const_bytes(section_contents));
|
||||
llvm::LLVMSetGlobalConstant(section_var, llvm::True);
|
||||
llvm::LLVMSetUnnamedAddress(section_var, llvm::UnnamedAddr::Global);
|
||||
llvm::LLVMRustSetLinkage(section_var, llvm::Linkage::LinkOnceODRLinkage);
|
||||
llvm::set_linkage(section_var, llvm::Linkage::LinkOnceODRLinkage);
|
||||
// This should make sure that the whole section is not larger than
|
||||
// the string it contains. Otherwise we get a warning from GDB.
|
||||
llvm::LLVMSetAlignment(section_var, 1);
|
||||
|
@ -785,13 +785,12 @@ fn codegen_msvc_try<'ll>(
|
||||
let type_info =
|
||||
bx.const_struct(&[type_info_vtable, bx.const_null(bx.type_ptr()), type_name], false);
|
||||
let tydesc = bx.declare_global("__rust_panic_type_info", bx.val_ty(type_info));
|
||||
unsafe {
|
||||
llvm::LLVMRustSetLinkage(tydesc, llvm::Linkage::LinkOnceODRLinkage);
|
||||
if bx.cx.tcx.sess.target.supports_comdat() {
|
||||
llvm::SetUniqueComdat(bx.llmod, tydesc);
|
||||
}
|
||||
llvm::LLVMSetInitializer(tydesc, type_info);
|
||||
|
||||
llvm::set_linkage(tydesc, llvm::Linkage::LinkOnceODRLinkage);
|
||||
if bx.cx.tcx.sess.target.supports_comdat() {
|
||||
llvm::SetUniqueComdat(bx.llmod, tydesc);
|
||||
}
|
||||
unsafe { llvm::LLVMSetInitializer(tydesc, type_info) };
|
||||
|
||||
// The flag value of 8 indicates that we are catching the exception by
|
||||
// reference instead of by value. We can't use catch by value because
|
||||
@ -1064,7 +1063,7 @@ fn gen_fn<'ll, 'tcx>(
|
||||
cx.set_frame_pointer_type(llfn);
|
||||
cx.apply_target_cpu_attr(llfn);
|
||||
// FIXME(eddyb) find a nicer way to do this.
|
||||
unsafe { llvm::LLVMRustSetLinkage(llfn, llvm::Linkage::InternalLinkage) };
|
||||
llvm::set_linkage(llfn, llvm::Linkage::InternalLinkage);
|
||||
let llbb = Builder::append_block(cx, llfn, "entry-block");
|
||||
let bx = Builder::build(cx, llbb);
|
||||
codegen(bx);
|
||||
|
@ -1,9 +1,11 @@
|
||||
#![allow(non_camel_case_types)]
|
||||
#![allow(non_upper_case_globals)]
|
||||
|
||||
use std::fmt::Debug;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use libc::{c_char, c_int, c_uint, c_ulonglong, c_void, size_t};
|
||||
use rustc_macros::TryFromU32;
|
||||
use rustc_target::spec::SymbolVisibility;
|
||||
|
||||
use super::RustString;
|
||||
@ -19,6 +21,30 @@ pub type Bool = c_uint;
|
||||
pub const True: Bool = 1 as Bool;
|
||||
pub const False: Bool = 0 as Bool;
|
||||
|
||||
/// Wrapper for a raw enum value returned from LLVM's C APIs.
|
||||
///
|
||||
/// For C enums returned by LLVM, it's risky to use a Rust enum as the return
|
||||
/// type, because it would be UB if a later version of LLVM adds a new enum
|
||||
/// value and returns it. Instead, return this raw wrapper, then convert to the
|
||||
/// Rust-side enum explicitly.
|
||||
#[repr(transparent)]
|
||||
pub struct RawEnum<T> {
|
||||
value: u32,
|
||||
/// We don't own or consume a `T`, but we can produce one.
|
||||
_rust_side_type: PhantomData<fn() -> T>,
|
||||
}
|
||||
|
||||
impl<T: TryFrom<u32>> RawEnum<T> {
|
||||
#[track_caller]
|
||||
pub(crate) fn to_rust(self) -> T
|
||||
where
|
||||
T::Error: Debug,
|
||||
{
|
||||
// If this fails, the Rust-side enum is out of sync with LLVM's enum.
|
||||
T::try_from(self.value).expect("enum value returned by LLVM should be known")
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq)]
|
||||
#[repr(C)]
|
||||
#[allow(dead_code)] // Variants constructed by C++.
|
||||
@ -108,26 +134,36 @@ pub enum CallConv {
|
||||
AvrInterrupt = 85,
|
||||
}
|
||||
|
||||
/// LLVMRustLinkage
|
||||
#[derive(Copy, Clone, PartialEq)]
|
||||
/// Must match the layout of `LLVMLinkage`.
|
||||
#[derive(Copy, Clone, PartialEq, TryFromU32)]
|
||||
#[repr(C)]
|
||||
pub enum Linkage {
|
||||
ExternalLinkage = 0,
|
||||
AvailableExternallyLinkage = 1,
|
||||
LinkOnceAnyLinkage = 2,
|
||||
LinkOnceODRLinkage = 3,
|
||||
WeakAnyLinkage = 4,
|
||||
WeakODRLinkage = 5,
|
||||
AppendingLinkage = 6,
|
||||
InternalLinkage = 7,
|
||||
PrivateLinkage = 8,
|
||||
ExternalWeakLinkage = 9,
|
||||
CommonLinkage = 10,
|
||||
#[deprecated = "marked obsolete by LLVM"]
|
||||
LinkOnceODRAutoHideLinkage = 4,
|
||||
WeakAnyLinkage = 5,
|
||||
WeakODRLinkage = 6,
|
||||
AppendingLinkage = 7,
|
||||
InternalLinkage = 8,
|
||||
PrivateLinkage = 9,
|
||||
#[deprecated = "marked obsolete by LLVM"]
|
||||
DLLImportLinkage = 10,
|
||||
#[deprecated = "marked obsolete by LLVM"]
|
||||
DLLExportLinkage = 11,
|
||||
ExternalWeakLinkage = 12,
|
||||
#[deprecated = "marked obsolete by LLVM"]
|
||||
GhostLinkage = 13,
|
||||
CommonLinkage = 14,
|
||||
LinkerPrivateLinkage = 15,
|
||||
LinkerPrivateWeakLinkage = 16,
|
||||
}
|
||||
|
||||
// LLVMRustVisibility
|
||||
/// Must match the layout of `LLVMVisibility`.
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, PartialEq)]
|
||||
#[derive(Copy, Clone, PartialEq, TryFromU32)]
|
||||
pub enum Visibility {
|
||||
Default = 0,
|
||||
Hidden = 1,
|
||||
@ -945,7 +981,11 @@ unsafe extern "C" {
|
||||
|
||||
// Operations on global variables, functions, and aliases (globals)
|
||||
pub fn LLVMIsDeclaration(Global: &Value) -> Bool;
|
||||
pub fn LLVMGetLinkage(Global: &Value) -> RawEnum<Linkage>;
|
||||
pub fn LLVMSetLinkage(Global: &Value, RustLinkage: Linkage);
|
||||
pub fn LLVMSetSection(Global: &Value, Section: *const c_char);
|
||||
pub fn LLVMGetVisibility(Global: &Value) -> RawEnum<Visibility>;
|
||||
pub fn LLVMSetVisibility(Global: &Value, Viz: Visibility);
|
||||
pub fn LLVMGetAlignment(Global: &Value) -> c_uint;
|
||||
pub fn LLVMSetAlignment(Global: &Value, Bytes: c_uint);
|
||||
pub fn LLVMSetDLLStorageClass(V: &Value, C: DLLStorageClass);
|
||||
@ -1521,10 +1561,6 @@ unsafe extern "C" {
|
||||
) -> bool;
|
||||
|
||||
// Operations on global variables, functions, and aliases (globals)
|
||||
pub fn LLVMRustGetLinkage(Global: &Value) -> Linkage;
|
||||
pub fn LLVMRustSetLinkage(Global: &Value, RustLinkage: Linkage);
|
||||
pub fn LLVMRustGetVisibility(Global: &Value) -> Visibility;
|
||||
pub fn LLVMRustSetVisibility(Global: &Value, Viz: Visibility);
|
||||
pub fn LLVMRustSetDSOLocal(Global: &Value, is_dso_local: bool);
|
||||
|
||||
// Operations on global variables
|
||||
|
@ -232,15 +232,23 @@ pub fn set_global_constant(llglobal: &Value, is_constant: bool) {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_linkage(llglobal: &Value) -> Linkage {
|
||||
unsafe { LLVMGetLinkage(llglobal) }.to_rust()
|
||||
}
|
||||
|
||||
pub fn set_linkage(llglobal: &Value, linkage: Linkage) {
|
||||
unsafe {
|
||||
LLVMRustSetLinkage(llglobal, linkage);
|
||||
LLVMSetLinkage(llglobal, linkage);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_visibility(llglobal: &Value) -> Visibility {
|
||||
unsafe { LLVMGetVisibility(llglobal) }.to_rust()
|
||||
}
|
||||
|
||||
pub fn set_visibility(llglobal: &Value, visibility: Visibility) {
|
||||
unsafe {
|
||||
LLVMRustSetVisibility(llglobal, visibility);
|
||||
LLVMSetVisibility(llglobal, visibility);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -39,9 +39,9 @@ impl<'tcx> PreDefineCodegenMethods<'tcx> for CodegenCx<'_, 'tcx> {
|
||||
.emit_fatal(SymbolAlreadyDefined { span: self.tcx.def_span(def_id), symbol_name })
|
||||
});
|
||||
|
||||
llvm::set_linkage(g, base::linkage_to_llvm(linkage));
|
||||
llvm::set_visibility(g, base::visibility_to_llvm(visibility));
|
||||
unsafe {
|
||||
llvm::LLVMRustSetLinkage(g, base::linkage_to_llvm(linkage));
|
||||
llvm::LLVMRustSetVisibility(g, base::visibility_to_llvm(visibility));
|
||||
if self.should_assume_dso_local(g, false) {
|
||||
llvm::LLVMRustSetDSOLocal(g, true);
|
||||
}
|
||||
@ -61,7 +61,7 @@ impl<'tcx> PreDefineCodegenMethods<'tcx> for CodegenCx<'_, 'tcx> {
|
||||
|
||||
let fn_abi = self.fn_abi_of_instance(instance, ty::List::empty());
|
||||
let lldecl = self.declare_fn(symbol_name, fn_abi, Some(instance));
|
||||
unsafe { llvm::LLVMRustSetLinkage(lldecl, base::linkage_to_llvm(linkage)) };
|
||||
llvm::set_linkage(lldecl, base::linkage_to_llvm(linkage));
|
||||
let attrs = self.tcx.codegen_fn_attrs(instance.def_id());
|
||||
base::set_link_section(lldecl, attrs);
|
||||
if (linkage == Linkage::LinkOnceODR || linkage == Linkage::WeakODR)
|
||||
@ -78,21 +78,15 @@ impl<'tcx> PreDefineCodegenMethods<'tcx> for CodegenCx<'_, 'tcx> {
|
||||
&& linkage != Linkage::Private
|
||||
&& self.tcx.is_compiler_builtins(LOCAL_CRATE)
|
||||
{
|
||||
unsafe {
|
||||
llvm::LLVMRustSetVisibility(lldecl, llvm::Visibility::Hidden);
|
||||
}
|
||||
llvm::set_visibility(lldecl, llvm::Visibility::Hidden);
|
||||
} else {
|
||||
unsafe {
|
||||
llvm::LLVMRustSetVisibility(lldecl, base::visibility_to_llvm(visibility));
|
||||
}
|
||||
llvm::set_visibility(lldecl, base::visibility_to_llvm(visibility));
|
||||
}
|
||||
|
||||
debug!("predefine_fn: instance = {:?}", instance);
|
||||
|
||||
unsafe {
|
||||
if self.should_assume_dso_local(lldecl, false) {
|
||||
llvm::LLVMRustSetDSOLocal(lldecl, true);
|
||||
}
|
||||
if self.should_assume_dso_local(lldecl, false) {
|
||||
unsafe { llvm::LLVMRustSetDSOLocal(lldecl, true) };
|
||||
}
|
||||
|
||||
self.instances.borrow_mut().insert(instance, lldecl);
|
||||
@ -102,13 +96,13 @@ impl<'tcx> PreDefineCodegenMethods<'tcx> for CodegenCx<'_, 'tcx> {
|
||||
impl CodegenCx<'_, '_> {
|
||||
/// Whether a definition or declaration can be assumed to be local to a group of
|
||||
/// libraries that form a single DSO or executable.
|
||||
pub(crate) unsafe fn should_assume_dso_local(
|
||||
pub(crate) fn should_assume_dso_local(
|
||||
&self,
|
||||
llval: &llvm::Value,
|
||||
is_declaration: bool,
|
||||
) -> bool {
|
||||
let linkage = unsafe { llvm::LLVMRustGetLinkage(llval) };
|
||||
let visibility = unsafe { llvm::LLVMRustGetVisibility(llval) };
|
||||
let linkage = llvm::get_linkage(llval);
|
||||
let visibility = llvm::get_visibility(llval);
|
||||
|
||||
if matches!(linkage, llvm::Linkage::InternalLinkage | llvm::Linkage::PrivateLinkage) {
|
||||
return true;
|
||||
|
@ -1619,86 +1619,6 @@ extern "C" void LLVMRustPositionBuilderAtStart(LLVMBuilderRef B,
|
||||
unwrap(B)->SetInsertPoint(unwrap(BB), Point);
|
||||
}
|
||||
|
||||
enum class LLVMRustLinkage {
|
||||
ExternalLinkage = 0,
|
||||
AvailableExternallyLinkage = 1,
|
||||
LinkOnceAnyLinkage = 2,
|
||||
LinkOnceODRLinkage = 3,
|
||||
WeakAnyLinkage = 4,
|
||||
WeakODRLinkage = 5,
|
||||
AppendingLinkage = 6,
|
||||
InternalLinkage = 7,
|
||||
PrivateLinkage = 8,
|
||||
ExternalWeakLinkage = 9,
|
||||
CommonLinkage = 10,
|
||||
};
|
||||
|
||||
static LLVMRustLinkage toRust(LLVMLinkage Linkage) {
|
||||
switch (Linkage) {
|
||||
case LLVMExternalLinkage:
|
||||
return LLVMRustLinkage::ExternalLinkage;
|
||||
case LLVMAvailableExternallyLinkage:
|
||||
return LLVMRustLinkage::AvailableExternallyLinkage;
|
||||
case LLVMLinkOnceAnyLinkage:
|
||||
return LLVMRustLinkage::LinkOnceAnyLinkage;
|
||||
case LLVMLinkOnceODRLinkage:
|
||||
return LLVMRustLinkage::LinkOnceODRLinkage;
|
||||
case LLVMWeakAnyLinkage:
|
||||
return LLVMRustLinkage::WeakAnyLinkage;
|
||||
case LLVMWeakODRLinkage:
|
||||
return LLVMRustLinkage::WeakODRLinkage;
|
||||
case LLVMAppendingLinkage:
|
||||
return LLVMRustLinkage::AppendingLinkage;
|
||||
case LLVMInternalLinkage:
|
||||
return LLVMRustLinkage::InternalLinkage;
|
||||
case LLVMPrivateLinkage:
|
||||
return LLVMRustLinkage::PrivateLinkage;
|
||||
case LLVMExternalWeakLinkage:
|
||||
return LLVMRustLinkage::ExternalWeakLinkage;
|
||||
case LLVMCommonLinkage:
|
||||
return LLVMRustLinkage::CommonLinkage;
|
||||
default:
|
||||
report_fatal_error("Invalid LLVMRustLinkage value!");
|
||||
}
|
||||
}
|
||||
|
||||
static LLVMLinkage fromRust(LLVMRustLinkage Linkage) {
|
||||
switch (Linkage) {
|
||||
case LLVMRustLinkage::ExternalLinkage:
|
||||
return LLVMExternalLinkage;
|
||||
case LLVMRustLinkage::AvailableExternallyLinkage:
|
||||
return LLVMAvailableExternallyLinkage;
|
||||
case LLVMRustLinkage::LinkOnceAnyLinkage:
|
||||
return LLVMLinkOnceAnyLinkage;
|
||||
case LLVMRustLinkage::LinkOnceODRLinkage:
|
||||
return LLVMLinkOnceODRLinkage;
|
||||
case LLVMRustLinkage::WeakAnyLinkage:
|
||||
return LLVMWeakAnyLinkage;
|
||||
case LLVMRustLinkage::WeakODRLinkage:
|
||||
return LLVMWeakODRLinkage;
|
||||
case LLVMRustLinkage::AppendingLinkage:
|
||||
return LLVMAppendingLinkage;
|
||||
case LLVMRustLinkage::InternalLinkage:
|
||||
return LLVMInternalLinkage;
|
||||
case LLVMRustLinkage::PrivateLinkage:
|
||||
return LLVMPrivateLinkage;
|
||||
case LLVMRustLinkage::ExternalWeakLinkage:
|
||||
return LLVMExternalWeakLinkage;
|
||||
case LLVMRustLinkage::CommonLinkage:
|
||||
return LLVMCommonLinkage;
|
||||
}
|
||||
report_fatal_error("Invalid LLVMRustLinkage value!");
|
||||
}
|
||||
|
||||
extern "C" LLVMRustLinkage LLVMRustGetLinkage(LLVMValueRef V) {
|
||||
return toRust(LLVMGetLinkage(V));
|
||||
}
|
||||
|
||||
extern "C" void LLVMRustSetLinkage(LLVMValueRef V,
|
||||
LLVMRustLinkage RustLinkage) {
|
||||
LLVMSetLinkage(V, fromRust(RustLinkage));
|
||||
}
|
||||
|
||||
extern "C" bool LLVMRustConstIntGetZExtValue(LLVMValueRef CV, uint64_t *value) {
|
||||
auto C = unwrap<llvm::ConstantInt>(CV);
|
||||
if (C->getBitWidth() > 64)
|
||||
@ -1726,45 +1646,6 @@ extern "C" bool LLVMRustConstInt128Get(LLVMValueRef CV, bool sext,
|
||||
return true;
|
||||
}
|
||||
|
||||
enum class LLVMRustVisibility {
|
||||
Default = 0,
|
||||
Hidden = 1,
|
||||
Protected = 2,
|
||||
};
|
||||
|
||||
static LLVMRustVisibility toRust(LLVMVisibility Vis) {
|
||||
switch (Vis) {
|
||||
case LLVMDefaultVisibility:
|
||||
return LLVMRustVisibility::Default;
|
||||
case LLVMHiddenVisibility:
|
||||
return LLVMRustVisibility::Hidden;
|
||||
case LLVMProtectedVisibility:
|
||||
return LLVMRustVisibility::Protected;
|
||||
}
|
||||
report_fatal_error("Invalid LLVMRustVisibility value!");
|
||||
}
|
||||
|
||||
static LLVMVisibility fromRust(LLVMRustVisibility Vis) {
|
||||
switch (Vis) {
|
||||
case LLVMRustVisibility::Default:
|
||||
return LLVMDefaultVisibility;
|
||||
case LLVMRustVisibility::Hidden:
|
||||
return LLVMHiddenVisibility;
|
||||
case LLVMRustVisibility::Protected:
|
||||
return LLVMProtectedVisibility;
|
||||
}
|
||||
report_fatal_error("Invalid LLVMRustVisibility value!");
|
||||
}
|
||||
|
||||
extern "C" LLVMRustVisibility LLVMRustGetVisibility(LLVMValueRef V) {
|
||||
return toRust(LLVMGetVisibility(V));
|
||||
}
|
||||
|
||||
extern "C" void LLVMRustSetVisibility(LLVMValueRef V,
|
||||
LLVMRustVisibility RustVisibility) {
|
||||
LLVMSetVisibility(V, fromRust(RustVisibility));
|
||||
}
|
||||
|
||||
extern "C" void LLVMRustSetDSOLocal(LLVMValueRef Global, bool is_dso_local) {
|
||||
unwrap<GlobalValue>(Global)->setDSOLocal(is_dso_local);
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ mod lift;
|
||||
mod query;
|
||||
mod serialize;
|
||||
mod symbols;
|
||||
mod try_from;
|
||||
mod type_foldable;
|
||||
mod type_visitable;
|
||||
|
||||
@ -165,3 +166,12 @@ decl_derive!(
|
||||
suggestion_part,
|
||||
applicability)] => diagnostics::subdiagnostic_derive
|
||||
);
|
||||
|
||||
decl_derive! {
|
||||
[TryFromU32] =>
|
||||
/// Derives `TryFrom<u32>` for the annotated `enum`, which must have no fields.
|
||||
/// Each variant maps to the value it would produce under an `as u32` cast.
|
||||
///
|
||||
/// The error type is `u32`.
|
||||
try_from::try_from_u32
|
||||
}
|
||||
|
55
compiler/rustc_macros/src/try_from.rs
Normal file
55
compiler/rustc_macros/src/try_from.rs
Normal file
@ -0,0 +1,55 @@
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::{quote, quote_spanned};
|
||||
use syn::Data;
|
||||
use syn::spanned::Spanned;
|
||||
use synstructure::Structure;
|
||||
|
||||
pub(crate) fn try_from_u32(s: Structure<'_>) -> TokenStream {
|
||||
let span_error = |span, message: &str| {
|
||||
quote_spanned! { span => const _: () = ::core::compile_error!(#message); }
|
||||
};
|
||||
|
||||
// Must be applied to an enum type.
|
||||
if let Some(span) = match &s.ast().data {
|
||||
Data::Enum(_) => None,
|
||||
Data::Struct(s) => Some(s.struct_token.span()),
|
||||
Data::Union(u) => Some(u.union_token.span()),
|
||||
} {
|
||||
return span_error(span, "type is not an enum (TryFromU32)");
|
||||
}
|
||||
|
||||
// The enum's variants must not have fields.
|
||||
let variant_field_errors = s
|
||||
.variants()
|
||||
.iter()
|
||||
.filter_map(|v| v.ast().fields.iter().map(|f| f.span()).next())
|
||||
.map(|span| span_error(span, "enum variant cannot have fields (TryFromU32)"))
|
||||
.collect::<TokenStream>();
|
||||
if !variant_field_errors.is_empty() {
|
||||
return variant_field_errors;
|
||||
}
|
||||
|
||||
let ctor = s
|
||||
.variants()
|
||||
.iter()
|
||||
.map(|v| v.construct(|_, _| -> TokenStream { unreachable!() }))
|
||||
.collect::<Vec<_>>();
|
||||
// FIXME(edition_2024): Fix the `keyword_idents_2024` lint to not trigger here?
|
||||
#[allow(keyword_idents_2024)]
|
||||
s.gen_impl(quote! {
|
||||
// The surrounding code might have shadowed these identifiers.
|
||||
use ::core::convert::TryFrom;
|
||||
use ::core::primitive::u32;
|
||||
use ::core::result::Result::{self, Ok, Err};
|
||||
|
||||
gen impl TryFrom<u32> for @Self {
|
||||
type Error = u32;
|
||||
|
||||
#[allow(deprecated)] // Don't warn about deprecated variants.
|
||||
fn try_from(value: u32) -> Result<Self, Self::Error> {
|
||||
#( if value == const { #ctor as u32 } { return Ok(#ctor) } )*
|
||||
Err(value)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
24
tests/ui-fulldeps/try-from-u32/errors.rs
Normal file
24
tests/ui-fulldeps/try-from-u32/errors.rs
Normal file
@ -0,0 +1,24 @@
|
||||
#![feature(rustc_private)]
|
||||
//@ edition: 2021
|
||||
|
||||
// Checks the error messages produced by `#[derive(TryFromU32)]`.
|
||||
|
||||
extern crate rustc_macros;
|
||||
|
||||
use rustc_macros::TryFromU32;
|
||||
|
||||
#[derive(TryFromU32)]
|
||||
struct MyStruct {} //~ type is not an enum
|
||||
|
||||
#[derive(TryFromU32)]
|
||||
enum NonTrivial {
|
||||
A,
|
||||
B(),
|
||||
C {},
|
||||
D(bool), //~ enum variant cannot have fields
|
||||
E(bool, bool), //~ enum variant cannot have fields
|
||||
F { x: bool }, //~ enum variant cannot have fields
|
||||
G { x: bool, y: bool }, //~ enum variant cannot have fields
|
||||
}
|
||||
|
||||
fn main() {}
|
32
tests/ui-fulldeps/try-from-u32/errors.stderr
Normal file
32
tests/ui-fulldeps/try-from-u32/errors.stderr
Normal file
@ -0,0 +1,32 @@
|
||||
error: type is not an enum (TryFromU32)
|
||||
--> $DIR/errors.rs:11:1
|
||||
|
|
||||
LL | struct MyStruct {}
|
||||
| ^^^^^^
|
||||
|
||||
error: enum variant cannot have fields (TryFromU32)
|
||||
--> $DIR/errors.rs:18:7
|
||||
|
|
||||
LL | D(bool),
|
||||
| ^^^^
|
||||
|
||||
error: enum variant cannot have fields (TryFromU32)
|
||||
--> $DIR/errors.rs:19:7
|
||||
|
|
||||
LL | E(bool, bool),
|
||||
| ^^^^
|
||||
|
||||
error: enum variant cannot have fields (TryFromU32)
|
||||
--> $DIR/errors.rs:20:9
|
||||
|
|
||||
LL | F { x: bool },
|
||||
| ^
|
||||
|
||||
error: enum variant cannot have fields (TryFromU32)
|
||||
--> $DIR/errors.rs:21:9
|
||||
|
|
||||
LL | G { x: bool, y: bool },
|
||||
| ^
|
||||
|
||||
error: aborting due to 5 previous errors
|
||||
|
32
tests/ui-fulldeps/try-from-u32/hygiene.rs
Normal file
32
tests/ui-fulldeps/try-from-u32/hygiene.rs
Normal file
@ -0,0 +1,32 @@
|
||||
#![feature(rustc_private)]
|
||||
//@ edition: 2021
|
||||
//@ check-pass
|
||||
|
||||
// Checks that the derive macro still works even if the surrounding code has
|
||||
// shadowed the relevant library types.
|
||||
|
||||
extern crate rustc_macros;
|
||||
|
||||
mod submod {
|
||||
use rustc_macros::TryFromU32;
|
||||
|
||||
struct Result;
|
||||
trait TryFrom {}
|
||||
#[allow(non_camel_case_types)]
|
||||
struct u32;
|
||||
struct Ok;
|
||||
struct Err;
|
||||
mod core {}
|
||||
mod std {}
|
||||
|
||||
#[derive(TryFromU32)]
|
||||
pub(crate) enum MyEnum {
|
||||
Zero,
|
||||
One,
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
use submod::MyEnum;
|
||||
let _: Result<MyEnum, u32> = MyEnum::try_from(1u32);
|
||||
}
|
36
tests/ui-fulldeps/try-from-u32/values.rs
Normal file
36
tests/ui-fulldeps/try-from-u32/values.rs
Normal file
@ -0,0 +1,36 @@
|
||||
#![feature(assert_matches)]
|
||||
#![feature(rustc_private)]
|
||||
//@ edition: 2021
|
||||
//@ run-pass
|
||||
|
||||
// Checks the values accepted by the `TryFrom<u32>` impl produced by `#[derive(TryFromU32)]`.
|
||||
|
||||
extern crate rustc_macros;
|
||||
|
||||
use core::assert_matches::assert_matches;
|
||||
use rustc_macros::TryFromU32;
|
||||
|
||||
#[derive(TryFromU32, Debug, PartialEq)]
|
||||
#[repr(u32)]
|
||||
enum Repr {
|
||||
Zero,
|
||||
One(),
|
||||
Seven = 7,
|
||||
}
|
||||
|
||||
#[derive(TryFromU32, Debug)]
|
||||
enum NoRepr {
|
||||
Zero,
|
||||
One,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
assert_eq!(Repr::try_from(0u32), Ok(Repr::Zero));
|
||||
assert_eq!(Repr::try_from(1u32), Ok(Repr::One()));
|
||||
assert_eq!(Repr::try_from(2u32), Err(2));
|
||||
assert_eq!(Repr::try_from(7u32), Ok(Repr::Seven));
|
||||
|
||||
assert_matches!(NoRepr::try_from(0u32), Ok(NoRepr::Zero));
|
||||
assert_matches!(NoRepr::try_from(1u32), Ok(NoRepr::One));
|
||||
assert_matches!(NoRepr::try_from(2u32), Err(2));
|
||||
}
|
Loading…
Reference in New Issue
Block a user