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
18 changed files with 321 additions and 229 deletions
|
@ -77,20 +77,14 @@ pub(crate) unsafe fn codegen(
|
||||||
// __rust_alloc_error_handler_should_panic
|
// __rust_alloc_error_handler_should_panic
|
||||||
let name = OomStrategy::SYMBOL;
|
let name = OomStrategy::SYMBOL;
|
||||||
let ll_g = llvm::LLVMRustGetOrInsertGlobal(llmod, name.as_ptr().cast(), name.len(), i8);
|
let ll_g = llvm::LLVMRustGetOrInsertGlobal(llmod, name.as_ptr().cast(), name.len(), i8);
|
||||||
llvm::LLVMRustSetVisibility(
|
llvm::set_visibility(ll_g, llvm::Visibility::from_generic(tcx.sess.default_visibility()));
|
||||||
ll_g,
|
|
||||||
llvm::Visibility::from_generic(tcx.sess.default_visibility()),
|
|
||||||
);
|
|
||||||
let val = tcx.sess.opts.unstable_opts.oom.should_panic();
|
let val = tcx.sess.opts.unstable_opts.oom.should_panic();
|
||||||
let llval = llvm::LLVMConstInt(i8, val as u64, False);
|
let llval = llvm::LLVMConstInt(i8, val as u64, False);
|
||||||
llvm::LLVMSetInitializer(ll_g, llval);
|
llvm::LLVMSetInitializer(ll_g, llval);
|
||||||
|
|
||||||
let name = NO_ALLOC_SHIM_IS_UNSTABLE;
|
let name = NO_ALLOC_SHIM_IS_UNSTABLE;
|
||||||
let ll_g = llvm::LLVMRustGetOrInsertGlobal(llmod, name.as_ptr().cast(), name.len(), i8);
|
let ll_g = llvm::LLVMRustGetOrInsertGlobal(llmod, name.as_ptr().cast(), name.len(), i8);
|
||||||
llvm::LLVMRustSetVisibility(
|
llvm::set_visibility(ll_g, llvm::Visibility::from_generic(tcx.sess.default_visibility()));
|
||||||
ll_g,
|
|
||||||
llvm::Visibility::from_generic(tcx.sess.default_visibility()),
|
|
||||||
);
|
|
||||||
let llval = llvm::LLVMConstInt(i8, 0, False);
|
let llval = llvm::LLVMConstInt(i8, 0, False);
|
||||||
llvm::LLVMSetInitializer(ll_g, llval);
|
llvm::LLVMSetInitializer(ll_g, llval);
|
||||||
}
|
}
|
||||||
|
@ -134,10 +128,7 @@ fn create_wrapper_function(
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
llvm::LLVMRustSetVisibility(
|
llvm::set_visibility(llfn, llvm::Visibility::from_generic(tcx.sess.default_visibility()));
|
||||||
llfn,
|
|
||||||
llvm::Visibility::from_generic(tcx.sess.default_visibility()),
|
|
||||||
);
|
|
||||||
|
|
||||||
if tcx.sess.must_emit_unwind_tables() {
|
if tcx.sess.must_emit_unwind_tables() {
|
||||||
let uwtable =
|
let uwtable =
|
||||||
|
@ -151,7 +142,7 @@ fn create_wrapper_function(
|
||||||
// -> ! DIFlagNoReturn
|
// -> ! DIFlagNoReturn
|
||||||
attributes::apply_to_llfn(callee, llvm::AttributePlace::Function, &[no_return]);
|
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());
|
let llbb = llvm::LLVMAppendBasicBlockInContext(llcx, llfn, c"entry".as_ptr());
|
||||||
|
|
||||||
|
|
|
@ -1043,7 +1043,7 @@ unsafe fn embed_bitcode(
|
||||||
|
|
||||||
let section = bitcode_section_name(cgcx);
|
let section = bitcode_section_name(cgcx);
|
||||||
llvm::LLVMSetSection(llglobal, section.as_ptr().cast());
|
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);
|
llvm::LLVMSetGlobalConstant(llglobal, llvm::True);
|
||||||
|
|
||||||
let llconst = common::bytes_in_context(llcx, cmdline.as_bytes());
|
let llconst = common::bytes_in_context(llcx, cmdline.as_bytes());
|
||||||
|
@ -1061,7 +1061,7 @@ unsafe fn embed_bitcode(
|
||||||
c".llvmcmd"
|
c".llvmcmd"
|
||||||
};
|
};
|
||||||
llvm::LLVMSetSection(llglobal, section.as_ptr());
|
llvm::LLVMSetSection(llglobal, section.as_ptr());
|
||||||
llvm::LLVMRustSetLinkage(llglobal, llvm::Linkage::PrivateLinkage);
|
llvm::set_linkage(llglobal, llvm::Linkage::PrivateLinkage);
|
||||||
} else {
|
} else {
|
||||||
// We need custom section flags, so emit module-level inline assembly.
|
// We need custom section flags, so emit module-level inline assembly.
|
||||||
let section_flags = if cgcx.is_pe_coff { "n" } else { "e" };
|
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 ptr_ty = Type::ptr_llcx(llcx);
|
||||||
let globals = base::iter_globals(llmod)
|
let globals = base::iter_globals(llmod)
|
||||||
.filter(|&val| {
|
.filter(|&val| {
|
||||||
llvm::LLVMRustGetLinkage(val) == llvm::Linkage::ExternalLinkage
|
llvm::get_linkage(val) == llvm::Linkage::ExternalLinkage
|
||||||
&& llvm::LLVMIsDeclaration(val) == 0
|
&& llvm::LLVMIsDeclaration(val) == 0
|
||||||
})
|
})
|
||||||
.filter_map(|val| {
|
.filter_map(|val| {
|
||||||
|
@ -1115,7 +1115,7 @@ fn create_msvc_imps(
|
||||||
for (imp_name, val) in globals {
|
for (imp_name, val) in globals {
|
||||||
let imp = llvm::LLVMAddGlobal(llmod, ptr_ty, imp_name.as_ptr());
|
let imp = llvm::LLVMAddGlobal(llmod, ptr_ty, imp_name.as_ptr());
|
||||||
llvm::LLVMSetInitializer(imp, val);
|
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
|
// 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
|
// that the visibility we apply to the declaration is the same one that
|
||||||
// has been applied to the definition (wherever that definition may be).
|
// 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_generic = instance.args.non_erasable_generics().next().is_some();
|
||||||
|
|
||||||
let is_hidden = if is_generic {
|
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))
|
|| !cx.tcx.is_reachable_non_generic(instance_def_id))
|
||||||
};
|
};
|
||||||
if is_hidden {
|
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
|
// 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::LLVMSetInitializer(g, sc);
|
||||||
llvm::LLVMSetGlobalConstant(g, True);
|
llvm::LLVMSetGlobalConstant(g, True);
|
||||||
llvm::LLVMSetUnnamedAddress(g, llvm::UnnamedAddr::Global);
|
llvm::LLVMSetUnnamedAddress(g, llvm::UnnamedAddr::Global);
|
||||||
llvm::LLVMRustSetLinkage(g, llvm::Linkage::InternalLinkage);
|
|
||||||
}
|
}
|
||||||
|
llvm::set_linkage(g, llvm::Linkage::InternalLinkage);
|
||||||
(s.to_owned(), g)
|
(s.to_owned(), g)
|
||||||
})
|
})
|
||||||
.1;
|
.1;
|
||||||
|
|
|
@ -172,29 +172,27 @@ fn check_and_apply_linkage<'ll, 'tcx>(
|
||||||
if let Some(linkage) = attrs.import_linkage {
|
if let Some(linkage) = attrs.import_linkage {
|
||||||
debug!("get_static: sym={} linkage={:?}", sym, linkage);
|
debug!("get_static: sym={} linkage={:?}", sym, linkage);
|
||||||
|
|
||||||
unsafe {
|
// Declare a symbol `foo` with the desired linkage.
|
||||||
// Declare a symbol `foo` with the desired linkage.
|
let g1 = cx.declare_global(sym, cx.type_i8());
|
||||||
let g1 = cx.declare_global(sym, cx.type_i8());
|
llvm::set_linkage(g1, base::linkage_to_llvm(linkage));
|
||||||
llvm::LLVMRustSetLinkage(g1, base::linkage_to_llvm(linkage));
|
|
||||||
|
|
||||||
// Declare an internal global `extern_with_linkage_foo` which
|
// Declare an internal global `extern_with_linkage_foo` which
|
||||||
// is initialized with the address of `foo`. If `foo` is
|
// is initialized with the address of `foo`. If `foo` is
|
||||||
// discarded during linking (for example, if `foo` has weak
|
// discarded during linking (for example, if `foo` has weak
|
||||||
// linkage and there are no definitions), then
|
// linkage and there are no definitions), then
|
||||||
// `extern_with_linkage_foo` will instead be initialized to
|
// `extern_with_linkage_foo` will instead be initialized to
|
||||||
// zero.
|
// zero.
|
||||||
let mut real_name = "_rust_extern_with_linkage_".to_string();
|
let mut real_name = "_rust_extern_with_linkage_".to_string();
|
||||||
real_name.push_str(sym);
|
real_name.push_str(sym);
|
||||||
let g2 = cx.define_global(&real_name, llty).unwrap_or_else(|| {
|
let g2 = cx.define_global(&real_name, llty).unwrap_or_else(|| {
|
||||||
cx.sess().dcx().emit_fatal(SymbolAlreadyDefined {
|
cx.sess().dcx().emit_fatal(SymbolAlreadyDefined {
|
||||||
span: cx.tcx.def_span(def_id),
|
span: cx.tcx.def_span(def_id),
|
||||||
symbol_name: sym,
|
symbol_name: sym,
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
llvm::LLVMRustSetLinkage(g2, llvm::Linkage::InternalLinkage);
|
llvm::set_linkage(g2, llvm::Linkage::InternalLinkage);
|
||||||
llvm::LLVMSetInitializer(g2, g1);
|
unsafe { llvm::LLVMSetInitializer(g2, g1) };
|
||||||
g2
|
g2
|
||||||
}
|
|
||||||
} else if cx.tcx.sess.target.arch == "x86"
|
} else if cx.tcx.sess.target.arch == "x86"
|
||||||
&& let Some(dllimport) = crate::common::get_dllimport(cx.tcx, def_id, sym)
|
&& let Some(dllimport) = crate::common::get_dllimport(cx.tcx, def_id, sym)
|
||||||
{
|
{
|
||||||
|
@ -224,23 +222,21 @@ impl<'ll> CodegenCx<'ll, '_> {
|
||||||
align: Align,
|
align: Align,
|
||||||
kind: Option<&str>,
|
kind: Option<&str>,
|
||||||
) -> &'ll Value {
|
) -> &'ll Value {
|
||||||
unsafe {
|
let gv = match kind {
|
||||||
let gv = match kind {
|
Some(kind) if !self.tcx.sess.fewer_names() => {
|
||||||
Some(kind) if !self.tcx.sess.fewer_names() => {
|
let name = self.generate_local_symbol_name(kind);
|
||||||
let name = self.generate_local_symbol_name(kind);
|
let gv = self.define_global(&name, self.val_ty(cv)).unwrap_or_else(|| {
|
||||||
let gv = self.define_global(&name, self.val_ty(cv)).unwrap_or_else(|| {
|
bug!("symbol `{}` is already defined", name);
|
||||||
bug!("symbol `{}` is already defined", name);
|
});
|
||||||
});
|
llvm::set_linkage(gv, llvm::Linkage::PrivateLinkage);
|
||||||
llvm::LLVMRustSetLinkage(gv, llvm::Linkage::PrivateLinkage);
|
gv
|
||||||
gv
|
}
|
||||||
}
|
_ => self.define_private_global(self.val_ty(cv)),
|
||||||
_ => self.define_private_global(self.val_ty(cv)),
|
};
|
||||||
};
|
unsafe { llvm::LLVMSetInitializer(gv, cv) };
|
||||||
llvm::LLVMSetInitializer(gv, cv);
|
set_global_alignment(self, gv, align);
|
||||||
set_global_alignment(self, gv, align);
|
llvm::SetUnnamedAddress(gv, llvm::UnnamedAddr::Global);
|
||||||
llvm::SetUnnamedAddress(gv, llvm::UnnamedAddr::Global);
|
gv
|
||||||
gv
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[instrument(level = "debug", skip(self))]
|
#[instrument(level = "debug", skip(self))]
|
||||||
|
@ -292,9 +288,7 @@ impl<'ll> CodegenCx<'ll, '_> {
|
||||||
let g = self.declare_global(sym, llty);
|
let g = self.declare_global(sym, llty);
|
||||||
|
|
||||||
if !self.tcx.is_reachable_non_generic(def_id) {
|
if !self.tcx.is_reachable_non_generic(def_id) {
|
||||||
unsafe {
|
llvm::set_visibility(g, llvm::Visibility::Hidden);
|
||||||
llvm::LLVMRustSetVisibility(g, llvm::Visibility::Hidden);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
g
|
g
|
||||||
|
@ -312,7 +306,7 @@ impl<'ll> CodegenCx<'ll, '_> {
|
||||||
llvm::set_thread_local_mode(g, self.tls_model);
|
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 {
|
if dso_local {
|
||||||
unsafe {
|
unsafe {
|
||||||
llvm::LLVMRustSetDSOLocal(g, true);
|
llvm::LLVMRustSetDSOLocal(g, true);
|
||||||
|
@ -401,8 +395,8 @@ impl<'ll> CodegenCx<'ll, '_> {
|
||||||
let name = llvm::get_value_name(g).to_vec();
|
let name = llvm::get_value_name(g).to_vec();
|
||||||
llvm::set_value_name(g, b"");
|
llvm::set_value_name(g, b"");
|
||||||
|
|
||||||
let linkage = llvm::LLVMRustGetLinkage(g);
|
let linkage = llvm::get_linkage(g);
|
||||||
let visibility = llvm::LLVMRustGetVisibility(g);
|
let visibility = llvm::get_visibility(g);
|
||||||
|
|
||||||
let new_g = llvm::LLVMRustGetOrInsertGlobal(
|
let new_g = llvm::LLVMRustGetOrInsertGlobal(
|
||||||
self.llmod,
|
self.llmod,
|
||||||
|
@ -411,8 +405,8 @@ impl<'ll> CodegenCx<'ll, '_> {
|
||||||
val_llty,
|
val_llty,
|
||||||
);
|
);
|
||||||
|
|
||||||
llvm::LLVMRustSetLinkage(new_g, linkage);
|
llvm::set_linkage(new_g, linkage);
|
||||||
llvm::LLVMRustSetVisibility(new_g, visibility);
|
llvm::set_visibility(new_g, visibility);
|
||||||
|
|
||||||
// The old global has had its name removed but is returned by
|
// The old global has had its name removed but is returned by
|
||||||
// get_static since it is in the instance cache. Provide an
|
// get_static since it is in the instance cache. Provide an
|
||||||
|
|
|
@ -605,7 +605,7 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
|
||||||
unsafe {
|
unsafe {
|
||||||
let g = llvm::LLVMAddGlobal(self.llmod, self.val_ty(array), name.as_ptr());
|
let g = llvm::LLVMAddGlobal(self.llmod, self.val_ty(array), name.as_ptr());
|
||||||
llvm::LLVMSetInitializer(g, array);
|
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());
|
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::LLVMSetInitializer(section_var, cx.const_bytes(section_contents));
|
||||||
llvm::LLVMSetGlobalConstant(section_var, llvm::True);
|
llvm::LLVMSetGlobalConstant(section_var, llvm::True);
|
||||||
llvm::LLVMSetUnnamedAddress(section_var, llvm::UnnamedAddr::Global);
|
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
|
// This should make sure that the whole section is not larger than
|
||||||
// the string it contains. Otherwise we get a warning from GDB.
|
// the string it contains. Otherwise we get a warning from GDB.
|
||||||
llvm::LLVMSetAlignment(section_var, 1);
|
llvm::LLVMSetAlignment(section_var, 1);
|
||||||
|
|
|
@ -785,13 +785,12 @@ fn codegen_msvc_try<'ll>(
|
||||||
let type_info =
|
let type_info =
|
||||||
bx.const_struct(&[type_info_vtable, bx.const_null(bx.type_ptr()), type_name], false);
|
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));
|
let tydesc = bx.declare_global("__rust_panic_type_info", bx.val_ty(type_info));
|
||||||
unsafe {
|
|
||||||
llvm::LLVMRustSetLinkage(tydesc, llvm::Linkage::LinkOnceODRLinkage);
|
llvm::set_linkage(tydesc, llvm::Linkage::LinkOnceODRLinkage);
|
||||||
if bx.cx.tcx.sess.target.supports_comdat() {
|
if bx.cx.tcx.sess.target.supports_comdat() {
|
||||||
llvm::SetUniqueComdat(bx.llmod, tydesc);
|
llvm::SetUniqueComdat(bx.llmod, tydesc);
|
||||||
}
|
|
||||||
llvm::LLVMSetInitializer(tydesc, type_info);
|
|
||||||
}
|
}
|
||||||
|
unsafe { llvm::LLVMSetInitializer(tydesc, type_info) };
|
||||||
|
|
||||||
// The flag value of 8 indicates that we are catching the exception by
|
// 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
|
// 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.set_frame_pointer_type(llfn);
|
||||||
cx.apply_target_cpu_attr(llfn);
|
cx.apply_target_cpu_attr(llfn);
|
||||||
// FIXME(eddyb) find a nicer way to do this.
|
// 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 llbb = Builder::append_block(cx, llfn, "entry-block");
|
||||||
let bx = Builder::build(cx, llbb);
|
let bx = Builder::build(cx, llbb);
|
||||||
codegen(bx);
|
codegen(bx);
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
#![allow(non_camel_case_types)]
|
#![allow(non_camel_case_types)]
|
||||||
#![allow(non_upper_case_globals)]
|
#![allow(non_upper_case_globals)]
|
||||||
|
|
||||||
|
use std::fmt::Debug;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
use libc::{c_char, c_int, c_uint, c_ulonglong, c_void, size_t};
|
use libc::{c_char, c_int, c_uint, c_ulonglong, c_void, size_t};
|
||||||
|
use rustc_macros::TryFromU32;
|
||||||
use rustc_target::spec::SymbolVisibility;
|
use rustc_target::spec::SymbolVisibility;
|
||||||
|
|
||||||
use super::RustString;
|
use super::RustString;
|
||||||
|
@ -19,6 +21,30 @@ pub type Bool = c_uint;
|
||||||
pub const True: Bool = 1 as Bool;
|
pub const True: Bool = 1 as Bool;
|
||||||
pub const False: Bool = 0 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)]
|
#[derive(Copy, Clone, PartialEq)]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[allow(dead_code)] // Variants constructed by C++.
|
#[allow(dead_code)] // Variants constructed by C++.
|
||||||
|
@ -108,26 +134,36 @@ pub enum CallConv {
|
||||||
AvrInterrupt = 85,
|
AvrInterrupt = 85,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// LLVMRustLinkage
|
/// Must match the layout of `LLVMLinkage`.
|
||||||
#[derive(Copy, Clone, PartialEq)]
|
#[derive(Copy, Clone, PartialEq, TryFromU32)]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub enum Linkage {
|
pub enum Linkage {
|
||||||
ExternalLinkage = 0,
|
ExternalLinkage = 0,
|
||||||
AvailableExternallyLinkage = 1,
|
AvailableExternallyLinkage = 1,
|
||||||
LinkOnceAnyLinkage = 2,
|
LinkOnceAnyLinkage = 2,
|
||||||
LinkOnceODRLinkage = 3,
|
LinkOnceODRLinkage = 3,
|
||||||
WeakAnyLinkage = 4,
|
#[deprecated = "marked obsolete by LLVM"]
|
||||||
WeakODRLinkage = 5,
|
LinkOnceODRAutoHideLinkage = 4,
|
||||||
AppendingLinkage = 6,
|
WeakAnyLinkage = 5,
|
||||||
InternalLinkage = 7,
|
WeakODRLinkage = 6,
|
||||||
PrivateLinkage = 8,
|
AppendingLinkage = 7,
|
||||||
ExternalWeakLinkage = 9,
|
InternalLinkage = 8,
|
||||||
CommonLinkage = 10,
|
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)]
|
#[repr(C)]
|
||||||
#[derive(Copy, Clone, PartialEq)]
|
#[derive(Copy, Clone, PartialEq, TryFromU32)]
|
||||||
pub enum Visibility {
|
pub enum Visibility {
|
||||||
Default = 0,
|
Default = 0,
|
||||||
Hidden = 1,
|
Hidden = 1,
|
||||||
|
@ -945,7 +981,11 @@ unsafe extern "C" {
|
||||||
|
|
||||||
// Operations on global variables, functions, and aliases (globals)
|
// Operations on global variables, functions, and aliases (globals)
|
||||||
pub fn LLVMIsDeclaration(Global: &Value) -> Bool;
|
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 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 LLVMGetAlignment(Global: &Value) -> c_uint;
|
||||||
pub fn LLVMSetAlignment(Global: &Value, Bytes: c_uint);
|
pub fn LLVMSetAlignment(Global: &Value, Bytes: c_uint);
|
||||||
pub fn LLVMSetDLLStorageClass(V: &Value, C: DLLStorageClass);
|
pub fn LLVMSetDLLStorageClass(V: &Value, C: DLLStorageClass);
|
||||||
|
@ -1521,10 +1561,6 @@ unsafe extern "C" {
|
||||||
) -> bool;
|
) -> bool;
|
||||||
|
|
||||||
// Operations on global variables, functions, and aliases (globals)
|
// 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);
|
pub fn LLVMRustSetDSOLocal(Global: &Value, is_dso_local: bool);
|
||||||
|
|
||||||
// Operations on global variables
|
// 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) {
|
pub fn set_linkage(llglobal: &Value, linkage: Linkage) {
|
||||||
unsafe {
|
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) {
|
pub fn set_visibility(llglobal: &Value, visibility: Visibility) {
|
||||||
unsafe {
|
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 })
|
.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 {
|
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) {
|
if self.should_assume_dso_local(g, false) {
|
||||||
llvm::LLVMRustSetDSOLocal(g, true);
|
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 fn_abi = self.fn_abi_of_instance(instance, ty::List::empty());
|
||||||
let lldecl = self.declare_fn(symbol_name, fn_abi, Some(instance));
|
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());
|
let attrs = self.tcx.codegen_fn_attrs(instance.def_id());
|
||||||
base::set_link_section(lldecl, attrs);
|
base::set_link_section(lldecl, attrs);
|
||||||
if (linkage == Linkage::LinkOnceODR || linkage == Linkage::WeakODR)
|
if (linkage == Linkage::LinkOnceODR || linkage == Linkage::WeakODR)
|
||||||
|
@ -78,21 +78,15 @@ impl<'tcx> PreDefineCodegenMethods<'tcx> for CodegenCx<'_, 'tcx> {
|
||||||
&& linkage != Linkage::Private
|
&& linkage != Linkage::Private
|
||||||
&& self.tcx.is_compiler_builtins(LOCAL_CRATE)
|
&& self.tcx.is_compiler_builtins(LOCAL_CRATE)
|
||||||
{
|
{
|
||||||
unsafe {
|
llvm::set_visibility(lldecl, llvm::Visibility::Hidden);
|
||||||
llvm::LLVMRustSetVisibility(lldecl, llvm::Visibility::Hidden);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
unsafe {
|
llvm::set_visibility(lldecl, base::visibility_to_llvm(visibility));
|
||||||
llvm::LLVMRustSetVisibility(lldecl, base::visibility_to_llvm(visibility));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
debug!("predefine_fn: instance = {:?}", instance);
|
debug!("predefine_fn: instance = {:?}", instance);
|
||||||
|
|
||||||
unsafe {
|
if self.should_assume_dso_local(lldecl, false) {
|
||||||
if self.should_assume_dso_local(lldecl, false) {
|
unsafe { llvm::LLVMRustSetDSOLocal(lldecl, true) };
|
||||||
llvm::LLVMRustSetDSOLocal(lldecl, true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.instances.borrow_mut().insert(instance, lldecl);
|
self.instances.borrow_mut().insert(instance, lldecl);
|
||||||
|
@ -102,13 +96,13 @@ impl<'tcx> PreDefineCodegenMethods<'tcx> for CodegenCx<'_, 'tcx> {
|
||||||
impl CodegenCx<'_, '_> {
|
impl CodegenCx<'_, '_> {
|
||||||
/// Whether a definition or declaration can be assumed to be local to a group of
|
/// Whether a definition or declaration can be assumed to be local to a group of
|
||||||
/// libraries that form a single DSO or executable.
|
/// libraries that form a single DSO or executable.
|
||||||
pub(crate) unsafe fn should_assume_dso_local(
|
pub(crate) fn should_assume_dso_local(
|
||||||
&self,
|
&self,
|
||||||
llval: &llvm::Value,
|
llval: &llvm::Value,
|
||||||
is_declaration: bool,
|
is_declaration: bool,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let linkage = unsafe { llvm::LLVMRustGetLinkage(llval) };
|
let linkage = llvm::get_linkage(llval);
|
||||||
let visibility = unsafe { llvm::LLVMRustGetVisibility(llval) };
|
let visibility = llvm::get_visibility(llval);
|
||||||
|
|
||||||
if matches!(linkage, llvm::Linkage::InternalLinkage | llvm::Linkage::PrivateLinkage) {
|
if matches!(linkage, llvm::Linkage::InternalLinkage | llvm::Linkage::PrivateLinkage) {
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -1619,86 +1619,6 @@ extern "C" void LLVMRustPositionBuilderAtStart(LLVMBuilderRef B,
|
||||||
unwrap(B)->SetInsertPoint(unwrap(BB), Point);
|
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) {
|
extern "C" bool LLVMRustConstIntGetZExtValue(LLVMValueRef CV, uint64_t *value) {
|
||||||
auto C = unwrap<llvm::ConstantInt>(CV);
|
auto C = unwrap<llvm::ConstantInt>(CV);
|
||||||
if (C->getBitWidth() > 64)
|
if (C->getBitWidth() > 64)
|
||||||
|
@ -1726,45 +1646,6 @@ extern "C" bool LLVMRustConstInt128Get(LLVMValueRef CV, bool sext,
|
||||||
return true;
|
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) {
|
extern "C" void LLVMRustSetDSOLocal(LLVMValueRef Global, bool is_dso_local) {
|
||||||
unwrap<GlobalValue>(Global)->setDSOLocal(is_dso_local);
|
unwrap<GlobalValue>(Global)->setDSOLocal(is_dso_local);
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,7 @@ mod lift;
|
||||||
mod query;
|
mod query;
|
||||||
mod serialize;
|
mod serialize;
|
||||||
mod symbols;
|
mod symbols;
|
||||||
|
mod try_from;
|
||||||
mod type_foldable;
|
mod type_foldable;
|
||||||
mod type_visitable;
|
mod type_visitable;
|
||||||
|
|
||||||
|
@ -165,3 +166,12 @@ decl_derive!(
|
||||||
suggestion_part,
|
suggestion_part,
|
||||||
applicability)] => diagnostics::subdiagnostic_derive
|
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…
Add table
Add a link
Reference in a new issue