initial implementation of the darwin_objc unstable feature

This commit is contained in:
Jo Bates
2025-06-04 19:49:13 -07:00
parent 02c7b1a7ac
commit 1ebf69d1b1
39 changed files with 1381 additions and 11 deletions

View File

@@ -122,6 +122,14 @@ attr_parsing_null_on_export = `export_name` may not contain null characters
attr_parsing_null_on_link_section = `link_section` may not contain null characters
attr_parsing_null_on_objc_class = `objc::class!` may not contain null characters
attr_parsing_null_on_objc_selector = `objc::selector!` may not contain null characters
attr_parsing_objc_class_expected_string_literal = `objc::class!` expected a string literal
attr_parsing_objc_selector_expected_string_literal = `objc::selector!` expected a string literal
attr_parsing_repr_ident =
meta item in `repr` must be an identifier

View File

@@ -2,7 +2,10 @@ use rustc_hir::attrs::{CoverageAttrKind, OptimizeAttr, SanitizerSet, UsedBy};
use rustc_session::parse::feature_err;
use super::prelude::*;
use crate::session_diagnostics::{NakedFunctionIncompatibleAttribute, NullOnExport};
use crate::session_diagnostics::{
NakedFunctionIncompatibleAttribute, NullOnExport, NullOnObjcClass, NullOnObjcSelector,
ObjcClassExpectedStringLiteral, ObjcSelectorExpectedStringLiteral,
};
pub(crate) struct OptimizeParser;
@@ -150,6 +153,70 @@ impl<S: Stage> SingleAttributeParser<S> for ExportNameParser {
}
}
pub(crate) struct ObjcClassParser;
impl<S: Stage> SingleAttributeParser<S> for ObjcClassParser {
const PATH: &[rustc_span::Symbol] = &[sym::rustc_objc_class];
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost;
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const ALLOWED_TARGETS: AllowedTargets =
AllowedTargets::AllowList(&[Allow(Target::ForeignStatic)]);
const TEMPLATE: AttributeTemplate = template!(NameValueStr: "ClassName");
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
let Some(nv) = args.name_value() else {
cx.expected_name_value(cx.attr_span, None);
return None;
};
let Some(classname) = nv.value_as_str() else {
// `#[rustc_objc_class = ...]` is expected to be used as an implementatioin detail
// inside a standard library macro, but `cx.expected_string_literal` exposes too much.
// Use a custom error message instead.
cx.emit_err(ObjcClassExpectedStringLiteral { span: nv.value_span });
return None;
};
if classname.as_str().contains('\0') {
// `#[rustc_objc_class = ...]` will be converted to a null-terminated string,
// so it may not contain any null characters.
cx.emit_err(NullOnObjcClass { span: nv.value_span });
return None;
}
Some(AttributeKind::ObjcClass { classname, span: cx.attr_span })
}
}
pub(crate) struct ObjcSelectorParser;
impl<S: Stage> SingleAttributeParser<S> for ObjcSelectorParser {
const PATH: &[rustc_span::Symbol] = &[sym::rustc_objc_selector];
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost;
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const ALLOWED_TARGETS: AllowedTargets =
AllowedTargets::AllowList(&[Allow(Target::ForeignStatic)]);
const TEMPLATE: AttributeTemplate = template!(NameValueStr: "methodName");
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
let Some(nv) = args.name_value() else {
cx.expected_name_value(cx.attr_span, None);
return None;
};
let Some(methname) = nv.value_as_str() else {
// `#[rustc_objc_selector = ...]` is expected to be used as an implementatioin detail
// inside a standard library macro, but `cx.expected_string_literal` exposes too much.
// Use a custom error message instead.
cx.emit_err(ObjcSelectorExpectedStringLiteral { span: nv.value_span });
return None;
};
if methname.as_str().contains('\0') {
// `#[rustc_objc_selector = ...]` will be converted to a null-terminated string,
// so it may not contain any null characters.
cx.emit_err(NullOnObjcSelector { span: nv.value_span });
return None;
}
Some(AttributeKind::ObjcSelector { methname, span: cx.attr_span })
}
}
#[derive(Default)]
pub(crate) struct NakedParser {
span: Option<Span>,

View File

@@ -20,8 +20,8 @@ use crate::attributes::allow_unstable::{
use crate::attributes::body::CoroutineParser;
use crate::attributes::codegen_attrs::{
ColdParser, CoverageParser, ExportNameParser, ForceTargetFeatureParser, NakedParser,
NoMangleParser, OptimizeParser, SanitizeParser, TargetFeatureParser, TrackCallerParser,
UsedParser,
NoMangleParser, ObjcClassParser, ObjcSelectorParser, OptimizeParser, SanitizeParser,
TargetFeatureParser, TrackCallerParser, UsedParser,
};
use crate::attributes::confusables::ConfusablesParser;
use crate::attributes::crate_level::{
@@ -187,6 +187,8 @@ attribute_parsers!(
Single<LinkageParser>,
Single<MoveSizeLimitParser>,
Single<MustUseParser>,
Single<ObjcClassParser>,
Single<ObjcSelectorParser>,
Single<OptimizeParser>,
Single<PathAttributeParser>,
Single<PatternComplexityLimitParser>,

View File

@@ -459,6 +459,34 @@ pub(crate) struct NullOnLinkSection {
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(attr_parsing_null_on_objc_class)]
pub(crate) struct NullOnObjcClass {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(attr_parsing_null_on_objc_selector)]
pub(crate) struct NullOnObjcSelector {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(attr_parsing_objc_class_expected_string_literal)]
pub(crate) struct ObjcClassExpectedStringLiteral {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(attr_parsing_objc_selector_expected_string_literal)]
pub(crate) struct ObjcSelectorExpectedStringLiteral {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(attr_parsing_stability_outside_std, code = E0734)]
pub(crate) struct StabilityOutsideStd {

View File

@@ -109,18 +109,36 @@ pub(crate) fn compile_codegen_unit(
attributes::apply_to_llfn(entry, llvm::AttributePlace::Function, &attrs);
}
// Define Objective-C module info and module flags. Note, the module info will
// also be added to the `llvm.compiler.used` variable, created later.
//
// These are only necessary when we need the linker to do its Objective-C-specific
// magic. We could theoretically do it unconditionally, but at a slight cost to linker
// performance in the common case where it's unnecessary.
if !cx.objc_classrefs.borrow().is_empty() || !cx.objc_selrefs.borrow().is_empty() {
if cx.objc_abi_version() == 1 {
cx.define_objc_module_info();
}
cx.add_objc_module_flags();
}
// Finalize code coverage by injecting the coverage map. Note, the coverage map will
// also be added to the `llvm.compiler.used` variable, created next.
if cx.sess().instrument_coverage() {
cx.coverageinfo_finalize();
}
// Create the llvm.used and llvm.compiler.used variables.
// Create the llvm.used variable.
if !cx.used_statics.is_empty() {
cx.create_used_variable_impl(c"llvm.used", &cx.used_statics);
}
if !cx.compiler_used_statics.is_empty() {
cx.create_used_variable_impl(c"llvm.compiler.used", &cx.compiler_used_statics);
// Create the llvm.compiler.used variable.
{
let compiler_used_statics = cx.compiler_used_statics.borrow();
if !compiler_used_statics.is_empty() {
cx.create_used_variable_impl(c"llvm.compiler.used", &compiler_used_statics);
}
}
// Run replace-all-uses-with for statics that need it. This must

View File

@@ -108,6 +108,10 @@ impl<'ll, CX: Borrow<SCx<'ll>>> GenericCx<'ll, CX> {
bytes_in_context(self.llcx(), bytes)
}
pub(crate) fn null_terminate_const_bytes(&self, bytes: &[u8]) -> &'ll Value {
null_terminate_bytes_in_context(self.llcx(), bytes)
}
pub(crate) fn const_get_elt(&self, v: &'ll Value, idx: u64) -> &'ll Value {
unsafe {
let idx = c_uint::try_from(idx).expect("LLVMGetAggregateElement index overflow");
@@ -381,6 +385,16 @@ pub(crate) fn bytes_in_context<'ll>(llcx: &'ll llvm::Context, bytes: &[u8]) -> &
}
}
pub(crate) fn null_terminate_bytes_in_context<'ll>(
llcx: &'ll llvm::Context,
bytes: &[u8],
) -> &'ll Value {
unsafe {
let ptr = bytes.as_ptr() as *const c_char;
llvm::LLVMConstStringInContext2(llcx, ptr, bytes.len(), FALSE)
}
}
pub(crate) fn named_struct<'ll>(ty: &'ll Type, elts: &[&'ll Value]) -> &'ll Value {
let len = c_uint::try_from(elts.len()).expect("LLVMConstStructInContext elements len overflow");
unsafe { llvm::LLVMConstNamedStruct(ty, elts.as_ptr(), len) }

View File

@@ -16,6 +16,7 @@ use rustc_middle::mir::mono::MonoItem;
use rustc_middle::ty::layout::{HasTypingEnv, LayoutOf};
use rustc_middle::ty::{self, Instance};
use rustc_middle::{bug, span_bug};
use rustc_span::Symbol;
use tracing::{debug, instrument, trace};
use crate::common::CodegenCx;
@@ -331,6 +332,10 @@ impl<'ll> CodegenCx<'ll, '_> {
}
g
} else if let Some(classname) = fn_attrs.objc_class {
self.get_objc_classref(classname)
} else if let Some(methname) = fn_attrs.objc_selector {
self.get_objc_selref(methname)
} else {
check_and_apply_linkage(self, fn_attrs, llty, sym, def_id)
};
@@ -543,8 +548,225 @@ impl<'ll> CodegenCx<'ll, '_> {
/// Add a global value to a list to be stored in the `llvm.compiler.used` variable,
/// an array of ptr.
pub(crate) fn add_compiler_used_global(&mut self, global: &'ll Value) {
self.compiler_used_statics.push(global);
pub(crate) fn add_compiler_used_global(&self, global: &'ll Value) {
self.compiler_used_statics.borrow_mut().push(global);
}
// We do our best here to match what Clang does when compiling Objective-C natively.
// See Clang's `CGObjCCommonMac::CreateCStringLiteral`:
// https://github.com/llvm/llvm-project/blob/llvmorg-20.1.8/clang/lib/CodeGen/CGObjCMac.cpp#L4134
fn define_objc_classname(&self, classname: &str) -> &'ll Value {
assert_eq!(self.objc_abi_version(), 1);
let llval = self.null_terminate_const_bytes(classname.as_bytes());
let llty = self.val_ty(llval);
let sym = self.generate_local_symbol_name("OBJC_CLASS_NAME_");
let g = self.define_global(&sym, llty).unwrap_or_else(|| {
bug!("symbol `{}` is already defined", sym);
});
set_global_alignment(self, g, self.tcx.data_layout.i8_align.abi);
llvm::set_initializer(g, llval);
llvm::set_linkage(g, llvm::Linkage::PrivateLinkage);
llvm::set_section(g, c"__TEXT,__cstring,cstring_literals");
llvm::LLVMSetGlobalConstant(g, llvm::TRUE);
llvm::LLVMSetUnnamedAddress(g, llvm::UnnamedAddr::Global);
self.add_compiler_used_global(g);
g
}
// We do our best here to match what Clang does when compiling Objective-C natively.
// See Clang's `ObjCNonFragileABITypesHelper`:
// https://github.com/llvm/llvm-project/blob/llvmorg-20.1.8/clang/lib/CodeGen/CGObjCMac.cpp#L6052
fn get_objc_class_t(&self) -> &'ll Type {
if let Some(class_t) = self.objc_class_t.get() {
return class_t;
}
assert_eq!(self.objc_abi_version(), 2);
// struct _class_t {
// struct _class_t* isa;
// struct _class_t* const superclass;
// void* cache;
// IMP* vtable;
// struct class_ro_t* ro;
// }
let class_t = self.type_named_struct("struct._class_t");
let els = [self.type_ptr(); 5];
let packed = false;
self.set_struct_body(class_t, &els, packed);
self.objc_class_t.set(Some(class_t));
class_t
}
// We do our best here to match what Clang does when compiling Objective-C natively. We
// deduplicate references within a CGU, but we need a reference definition in each referencing
// CGU. All attempts at using external references to a single reference definition result in
// linker errors.
fn get_objc_classref(&self, classname: Symbol) -> &'ll Value {
let mut classrefs = self.objc_classrefs.borrow_mut();
if let Some(classref) = classrefs.get(&classname).copied() {
return classref;
}
let g = match self.objc_abi_version() {
1 => {
// See Clang's `CGObjCMac::EmitClassRefFromId`:
// https://github.com/llvm/llvm-project/blob/llvmorg-20.1.8/clang/lib/CodeGen/CGObjCMac.cpp#L5205
let llval = self.define_objc_classname(classname.as_str());
let llty = self.type_ptr();
let sym = self.generate_local_symbol_name("OBJC_CLASS_REFERENCES_");
let g = self.define_global(&sym, llty).unwrap_or_else(|| {
bug!("symbol `{}` is already defined", sym);
});
set_global_alignment(self, g, self.tcx.data_layout.pointer_align().abi);
llvm::set_initializer(g, llval);
llvm::set_linkage(g, llvm::Linkage::PrivateLinkage);
llvm::set_section(g, c"__OBJC,__cls_refs,literal_pointers,no_dead_strip");
self.add_compiler_used_global(g);
g
}
2 => {
// See Clang's `CGObjCNonFragileABIMac::EmitClassRefFromId`:
// https://github.com/llvm/llvm-project/blob/llvmorg-20.1.8/clang/lib/CodeGen/CGObjCMac.cpp#L7423
let llval = {
let extern_sym = format!("OBJC_CLASS_$_{}", classname.as_str());
let extern_llty = self.get_objc_class_t();
self.declare_global(&extern_sym, extern_llty)
};
let llty = self.type_ptr();
let sym = self.generate_local_symbol_name("OBJC_CLASSLIST_REFERENCES_$_");
let g = self.define_global(&sym, llty).unwrap_or_else(|| {
bug!("symbol `{}` is already defined", sym);
});
set_global_alignment(self, g, self.tcx.data_layout.pointer_align().abi);
llvm::set_initializer(g, llval);
llvm::set_linkage(g, llvm::Linkage::InternalLinkage);
llvm::set_section(g, c"__DATA,__objc_classrefs,regular,no_dead_strip");
self.add_compiler_used_global(g);
g
}
_ => unreachable!(),
};
classrefs.insert(classname, g);
g
}
// We do our best here to match what Clang does when compiling Objective-C natively. We
// deduplicate references within a CGU, but we need a reference definition in each referencing
// CGU. All attempts at using external references to a single reference definition result in
// linker errors.
//
// Newer versions of Apple Clang generate calls to `@"objc_msgSend$methname"` selector stub
// functions. We don't currently do that. The code we generate is closer to what Apple Clang
// generates with the `-fno-objc-msgsend-selector-stubs` option.
fn get_objc_selref(&self, methname: Symbol) -> &'ll Value {
let mut selrefs = self.objc_selrefs.borrow_mut();
if let Some(selref) = selrefs.get(&methname).copied() {
return selref;
}
let abi_version = self.objc_abi_version();
// See Clang's `CGObjCCommonMac::CreateCStringLiteral`:
// https://github.com/llvm/llvm-project/blob/llvmorg-20.1.8/clang/lib/CodeGen/CGObjCMac.cpp#L4134
let methname_llval = self.null_terminate_const_bytes(methname.as_str().as_bytes());
let methname_llty = self.val_ty(methname_llval);
let methname_sym = self.generate_local_symbol_name("OBJC_METH_VAR_NAME_");
let methname_g = self.define_global(&methname_sym, methname_llty).unwrap_or_else(|| {
bug!("symbol `{}` is already defined", methname_sym);
});
set_global_alignment(self, methname_g, self.tcx.data_layout.i8_align.abi);
llvm::set_initializer(methname_g, methname_llval);
llvm::set_linkage(methname_g, llvm::Linkage::PrivateLinkage);
llvm::set_section(
methname_g,
match abi_version {
1 => c"__TEXT,__cstring,cstring_literals",
2 => c"__TEXT,__objc_methname,cstring_literals",
_ => unreachable!(),
},
);
llvm::LLVMSetGlobalConstant(methname_g, llvm::TRUE);
llvm::LLVMSetUnnamedAddress(methname_g, llvm::UnnamedAddr::Global);
self.add_compiler_used_global(methname_g);
// See Clang's `CGObjCMac::EmitSelectorAddr`:
// https://github.com/llvm/llvm-project/blob/llvmorg-20.1.8/clang/lib/CodeGen/CGObjCMac.cpp#L5243
// And Clang's `CGObjCNonFragileABIMac::EmitSelectorAddr`:
// https://github.com/llvm/llvm-project/blob/llvmorg-20.1.8/clang/lib/CodeGen/CGObjCMac.cpp#L7586
let selref_llval = methname_g;
let selref_llty = self.type_ptr();
let selref_sym = self.generate_local_symbol_name("OBJC_SELECTOR_REFERENCES_");
let selref_g = self.define_global(&selref_sym, selref_llty).unwrap_or_else(|| {
bug!("symbol `{}` is already defined", selref_sym);
});
set_global_alignment(self, selref_g, self.tcx.data_layout.pointer_align().abi);
llvm::set_initializer(selref_g, selref_llval);
llvm::set_externally_initialized(selref_g, true);
llvm::set_linkage(
selref_g,
match abi_version {
1 => llvm::Linkage::PrivateLinkage,
2 => llvm::Linkage::InternalLinkage,
_ => unreachable!(),
},
);
llvm::set_section(
selref_g,
match abi_version {
1 => c"__OBJC,__message_refs,literal_pointers,no_dead_strip",
2 => c"__DATA,__objc_selrefs,literal_pointers,no_dead_strip",
_ => unreachable!(),
},
);
self.add_compiler_used_global(selref_g);
selrefs.insert(methname, selref_g);
selref_g
}
// We do our best here to match what Clang does when compiling Objective-C natively.
// See Clang's `ObjCTypesHelper`:
// https://github.com/llvm/llvm-project/blob/llvmorg-20.1.8/clang/lib/CodeGen/CGObjCMac.cpp#L5936
// And Clang's `CGObjCMac::EmitModuleInfo`:
// https://github.com/llvm/llvm-project/blob/llvmorg-20.1.8/clang/lib/CodeGen/CGObjCMac.cpp#L5151
pub(crate) fn define_objc_module_info(&mut self) {
assert_eq!(self.objc_abi_version(), 1);
// struct _objc_module {
// long version; // Hardcoded to 7 in Clang.
// long size; // sizeof(struct _objc_module)
// char* name; // Hardcoded to classname "" in Clang.
// struct _objc_symtab* symtab; // Null without class or category definitions.
// }
let llty = self.type_named_struct("struct._objc_module");
let i32_llty = self.type_i32();
let ptr_llty = self.type_ptr();
let packed = false;
self.set_struct_body(llty, &[i32_llty, i32_llty, ptr_llty, ptr_llty], packed);
let version = self.const_uint(i32_llty, 7);
let size = self.const_uint(i32_llty, 16);
let name = self.define_objc_classname("");
let symtab = self.const_null(ptr_llty);
let llval = crate::common::named_struct(llty, &[version, size, name, symtab]);
let sym = "OBJC_MODULES";
let g = self.define_global(&sym, llty).unwrap_or_else(|| {
bug!("symbol `{}` is already defined", sym);
});
set_global_alignment(self, g, self.tcx.data_layout.pointer_align().abi);
llvm::set_initializer(g, llval);
llvm::set_linkage(g, llvm::Linkage::PrivateLinkage);
llvm::set_section(g, c"__OBJC,__module_info,regular,no_dead_strip");
self.add_compiler_used_global(g);
}
}

View File

@@ -26,7 +26,7 @@ use rustc_session::config::{
BranchProtection, CFGuard, CFProtection, CrateType, DebugInfo, FunctionReturn, PAuthKey, PacRet,
};
use rustc_span::source_map::Spanned;
use rustc_span::{DUMMY_SP, Span};
use rustc_span::{DUMMY_SP, Span, Symbol};
use rustc_symbol_mangling::mangle_internal_symbol;
use rustc_target::spec::{HasTargetSpec, RelocModel, SmallDataThresholdSupport, Target, TlsModel};
use smallvec::SmallVec;
@@ -119,7 +119,7 @@ pub(crate) struct FullCx<'ll, 'tcx> {
/// Statics that will be placed in the llvm.compiler.used variable
/// See <https://llvm.org/docs/LangRef.html#the-llvm-compiler-used-global-variable> for details
pub compiler_used_statics: Vec<&'ll Value>,
pub compiler_used_statics: RefCell<Vec<&'ll Value>>,
/// Mapping of non-scalar types to llvm types.
pub type_lowering: RefCell<FxHashMap<(Ty<'tcx>, Option<VariantIdx>), &'ll Type>>,
@@ -146,6 +146,15 @@ pub(crate) struct FullCx<'ll, 'tcx> {
/// `global_asm!` needs to be able to find this new global so that it can
/// compute the correct mangled symbol name to insert into the asm.
pub renamed_statics: RefCell<FxHashMap<DefId, &'ll Value>>,
/// Cached Objective-C class type
pub objc_class_t: Cell<Option<&'ll Type>>,
/// Cache of Objective-C class references
pub objc_classrefs: RefCell<FxHashMap<Symbol, &'ll Value>>,
/// Cache of Objective-C selector references
pub objc_selrefs: RefCell<FxHashMap<Symbol, &'ll Value>>,
}
fn to_llvm_tls_model(tls_model: TlsModel) -> llvm::ThreadLocalMode {
@@ -644,7 +653,7 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
const_globals: Default::default(),
statics_to_rauw: RefCell::new(Vec::new()),
used_statics: Vec::new(),
compiler_used_statics: Vec::new(),
compiler_used_statics: Default::default(),
type_lowering: Default::default(),
scalar_lltypes: Default::default(),
coverage_cx,
@@ -655,6 +664,9 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
intrinsics: Default::default(),
local_gen_sym_counter: Cell::new(0),
renamed_statics: Default::default(),
objc_class_t: Cell::new(None),
objc_classrefs: Default::default(),
objc_selrefs: Default::default(),
},
PhantomData,
)
@@ -679,6 +691,69 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
llvm::set_linkage(g, llvm::Linkage::AppendingLinkage);
llvm::set_section(g, c"llvm.metadata");
}
/// The Objective-C ABI that is used.
///
/// This corresponds to the `-fobjc-abi-version=` flag in Clang / GCC.
pub(crate) fn objc_abi_version(&self) -> u32 {
assert!(self.tcx.sess.target.is_like_darwin);
if self.tcx.sess.target.arch == "x86" && self.tcx.sess.target.os == "macos" {
// 32-bit x86 macOS uses ABI version 1 (a.k.a. the "fragile ABI").
1
} else {
// All other Darwin-like targets we support use ABI version 2
// (a.k.a the "non-fragile ABI").
2
}
}
// We do our best here to match what Clang does when compiling Objective-C natively.
// See Clang's `CGObjCCommonMac::EmitImageInfo`:
// https://github.com/llvm/llvm-project/blob/llvmorg-20.1.8/clang/lib/CodeGen/CGObjCMac.cpp#L5085
pub(crate) fn add_objc_module_flags(&self) {
let abi_version = self.objc_abi_version();
llvm::add_module_flag_u32(
self.llmod,
llvm::ModuleFlagMergeBehavior::Error,
"Objective-C Version",
abi_version,
);
llvm::add_module_flag_u32(
self.llmod,
llvm::ModuleFlagMergeBehavior::Error,
"Objective-C Image Info Version",
0,
);
llvm::add_module_flag_str(
self.llmod,
llvm::ModuleFlagMergeBehavior::Error,
"Objective-C Image Info Section",
match abi_version {
1 => "__OBJC,__image_info,regular",
2 => "__DATA,__objc_imageinfo,regular,no_dead_strip",
_ => unreachable!(),
},
);
if self.tcx.sess.target.env == "sim" {
llvm::add_module_flag_u32(
self.llmod,
llvm::ModuleFlagMergeBehavior::Error,
"Objective-C Is Simulated",
1 << 5,
);
}
llvm::add_module_flag_u32(
self.llmod,
llvm::ModuleFlagMergeBehavior::Error,
"Objective-C Class Properties",
1 << 6,
);
}
}
impl<'ll> SimpleCx<'ll> {
pub(crate) fn get_type_of_global(&self, val: &'ll Value) -> &'ll Type {

View File

@@ -1237,6 +1237,7 @@ unsafe extern "C" {
pub(crate) safe fn LLVMSetGlobalConstant(GlobalVar: &Value, IsConstant: Bool);
pub(crate) safe fn LLVMSetTailCall(CallInst: &Value, IsTailCall: Bool);
pub(crate) safe fn LLVMSetTailCallKind(CallInst: &Value, kind: TailCallKind);
pub(crate) safe fn LLVMSetExternallyInitialized(GlobalVar: &Value, IsExtInit: Bool);
// Operations on attributes
pub(crate) fn LLVMCreateStringAttribute(

View File

@@ -258,6 +258,10 @@ pub(crate) fn set_alignment(llglobal: &Value, align: Align) {
}
}
pub(crate) fn set_externally_initialized(llglobal: &Value, is_ext_init: bool) {
LLVMSetExternallyInitialized(llglobal, is_ext_init.to_llvm_bool());
}
/// Get the `name`d comdat from `llmod` and assign it to `llglobal`.
///
/// Inserts the comdat into `llmod` if it does not exist.

View File

@@ -296,6 +296,12 @@ fn process_builtin_attrs(
AttributeKind::Sanitize { span, .. } => {
interesting_spans.sanitize = Some(*span);
}
AttributeKind::ObjcClass { classname, .. } => {
codegen_fn_attrs.objc_class = Some(*classname);
}
AttributeKind::ObjcSelector { methname, .. } => {
codegen_fn_attrs.objc_selector = Some(*methname);
}
_ => {}
}
}

View File

@@ -1057,6 +1057,14 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
rustc_std_internal_symbol, Normal, template!(Word), WarnFollowing,
EncodeCrossCrate::No,
),
rustc_attr!(
rustc_objc_class, Normal, template!(NameValueStr: "ClassName"), ErrorPreceding,
EncodeCrossCrate::No,
),
rustc_attr!(
rustc_objc_selector, Normal, template!(NameValueStr: "methodName"), ErrorPreceding,
EncodeCrossCrate::No,
),
// ==========================================================================
// Internal attributes, Macro related:

View File

@@ -594,6 +594,12 @@ pub enum AttributeKind {
/// Represents `#[non_exhaustive]`
NonExhaustive(Span),
/// Represents `#[rustc_objc_class]`
ObjcClass { classname: Symbol, span: Span },
/// Represents `#[rustc_objc_selector]`
ObjcSelector { methname: Symbol, span: Span },
/// Represents `#[optimize(size|speed)]`
Optimize(OptimizeAttr, Span),

View File

@@ -69,6 +69,8 @@ impl AttributeKind {
NoMangle(..) => Yes, // Needed for rustdoc
NoStd(..) => No,
NonExhaustive(..) => Yes, // Needed for rustdoc
ObjcClass { .. } => No,
ObjcSelector { .. } => No,
Optimize(..) => No,
ParenSugar(..) => No,
PassByValue(..) => Yes,

View File

@@ -69,6 +69,10 @@ pub struct CodegenFnAttrs {
/// The `#[patchable_function_entry(...)]` attribute. Indicates how many nops should be around
/// the function entry.
pub patchable_function_entry: Option<PatchableFunctionEntry>,
/// The `#[rustc_objc_class = "..."]` attribute.
pub objc_class: Option<Symbol>,
/// The `#[rustc_objc_selector = "..."]` attribute.
pub objc_selector: Option<Symbol>,
}
#[derive(Copy, Clone, Debug, TyEncodable, TyDecodable, HashStable, PartialEq, Eq)]
@@ -185,6 +189,8 @@ impl CodegenFnAttrs {
instruction_set: None,
alignment: None,
patchable_function_entry: None,
objc_class: None,
objc_selector: None,
}
}

View File

@@ -276,6 +276,8 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
| AttributeKind::PatternComplexityLimit { .. }
| AttributeKind::NoCore { .. }
| AttributeKind::NoStd { .. }
| AttributeKind::ObjcClass { .. }
| AttributeKind::ObjcSelector { .. }
) => { /* do nothing */ }
Attribute::Unparsed(attr_item) => {
style = Some(attr_item.style);

View File

@@ -1917,6 +1917,8 @@ symbols! {
rustc_no_mir_inline,
rustc_nonnull_optimization_guaranteed,
rustc_nounwind,
rustc_objc_class,
rustc_objc_selector,
rustc_object_lifetime_default,
rustc_on_unimplemented,
rustc_outlives,

View File

@@ -313,6 +313,7 @@ pub mod io;
pub mod iter;
pub mod net;
pub mod option;
pub mod os;
pub mod panic;
pub mod panicking;
#[unstable(feature = "pattern_type_macro", issue = "123646")]

View File

@@ -0,0 +1,19 @@
//! Platform-specific extensions to `core` for Darwin / Apple platforms.
//!
//! This is available on the following operating systems:
//! - macOS
//! - iOS
//! - tvOS
//! - watchOS
//! - visionOS
//!
//! Note: This module is called "Darwin" as that's the name of the underlying
//! core OS of the above operating systems, but it should not be confused with
//! the `-darwin` suffix in the `x86_64-apple-darwin` and
//! `aarch64-apple-darwin` target names, which are mostly named that way for
//! legacy reasons.
#![unstable(feature = "darwin_objc", issue = "145496")]
#![doc(cfg(target_vendor = "apple"))]
pub mod objc;

View File

@@ -0,0 +1,113 @@
//! Defines types and macros for Objective-C interoperability.
#![unstable(feature = "darwin_objc", issue = "145496")]
#![allow(nonstandard_style)]
use crate::fmt;
/// Equivalent to Objective-Cs `struct objc_class` type.
#[cfg_attr(not(doc), repr(u8))] // An implementation detail we don't want to show up in rustdoc
pub enum objc_class {
#[unstable(
feature = "objc_class_variant",
reason = "temporary implementation detail",
issue = "none"
)]
#[doc(hidden)]
__variant1,
#[unstable(
feature = "objc_class_variant",
reason = "temporary implementation detail",
issue = "none"
)]
#[doc(hidden)]
__variant2,
}
impl fmt::Debug for objc_class {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("objc_class").finish()
}
}
/// Equivalent to Objective-Cs `struct objc_selector` type.
#[cfg_attr(not(doc), repr(u8))] // An implementation detail we don't want to show up in rustdoc
pub enum objc_selector {
#[unstable(
feature = "objc_selector_variant",
reason = "temporary implementation detail",
issue = "none"
)]
#[doc(hidden)]
__variant1,
#[unstable(
feature = "objc_selector_variant",
reason = "temporary implementation detail",
issue = "none"
)]
#[doc(hidden)]
__variant2,
}
impl fmt::Debug for objc_selector {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("objc_selector").finish()
}
}
/// Equivalent to Objective-Cs `Class` type.
pub type Class = *mut objc_class;
/// Equivalent to Objective-Cs `SEL` type.
pub type SEL = *mut objc_selector;
/// Gets a reference to an Objective-C class.
///
/// This macro will yield an expression of type [`Class`] for the given class name string literal.
///
/// # Example
///
/// ```no_run
/// #![feature(darwin_objc)]
/// use core::os::darwin::objc;
///
/// let string_class = objc::class!("NSString");
/// ```
#[allow_internal_unstable(rustc_attrs)]
pub macro class($classname:expr) {{
// Since static Objective-C class references actually end up with multiple definitions
// across dylib boundaries, we only expose the value of the static and don't provide a way to
// get the address of or a reference to the static.
unsafe extern "C" {
#[rustc_objc_class = $classname]
safe static VAL: $crate::os::darwin::objc::Class;
}
VAL
}}
/// Gets a reference to an Objective-C selector.
///
/// This macro will yield an expression of type [`SEL`] for the given method name string literal.
///
/// It is similar to Objective-Cs `@selector` directive.
///
/// # Examples
///
/// ```no_run
/// #![feature(darwin_objc)]
/// use core::os::darwin::objc;
///
/// let alloc_sel = objc::selector!("alloc");
/// let init_sel = objc::selector!("initWithCString:encoding:");
/// ```
#[allow_internal_unstable(rustc_attrs)]
pub macro selector($methname:expr) {{
// Since static Objective-C selector references actually end up with multiple definitions
// across dylib boundaries, we only expose the value of the static and don't provide a way to
// get the address of or a reference to the static.
unsafe extern "C" {
#[rustc_objc_selector = $methname]
safe static VAL: $crate::os::darwin::objc::SEL;
}
VAL
}}

View File

@@ -0,0 +1,24 @@
//! OS-specific functionality.
#![unstable(feature = "darwin_objc", issue = "145496")]
#[cfg(all(
doc,
any(
all(target_arch = "wasm32", not(target_os = "wasi")),
all(target_vendor = "fortanix", target_env = "sgx")
)
))]
#[unstable(issue = "none", feature = "std_internals")]
pub mod darwin {}
// darwin
#[cfg(not(all(
doc,
any(
all(target_arch = "wasm32", not(target_os = "wasi")),
all(target_vendor = "fortanix", target_env = "sgx")
)
)))]
#[cfg(any(target_vendor = "apple", doc))]
pub mod darwin;

View File

@@ -17,6 +17,8 @@
#![doc(cfg(target_vendor = "apple"))]
pub mod fs;
pub mod objc;
// deprecated, but used for public reexport under `std::os::unix::raw`, as
// well as `std::os::macos`/`std::os::ios`, because those modules precede the
// decision to remove these.

View File

@@ -0,0 +1,13 @@
//! Defines types and macros for Objective-C interoperability.
//!
//! This module re-exports all the items in [`core::os::darwin::objc`].
//!
//! [`core::os::darwin::objc`]: ../../../../core/os/darwin/objc/index.html "mod core::os::darwin::objc"
#![unstable(feature = "darwin_objc", issue = "145496")]
// We can't generate an intra-doc link for this automatically since `core::os::darwin` isn't
// compiled into `core` on every platform even though it's documented on every platform.
// We just link to it directly in the module documentation above instead.
#[cfg(not(doc))]
pub use core::os::darwin::objc::*;

View File

@@ -53,6 +53,7 @@ const EXCEPTION_PATHS: &[&str] = &[
// core::ffi contains platform-specific type and linkage configuration
"library/core/src/ffi/mod.rs",
"library/core/src/ffi/primitives.rs",
"library/core/src/os", // Platform-specific public interfaces
"library/std/src/sys", // Platform-specific code for std lives here.
"library/std/src/os", // Platform-specific public interfaces
// Temporary `std` exceptions

View File

@@ -0,0 +1,27 @@
#![crate_type = "lib"]
#![feature(darwin_objc)]
use std::os::darwin::objc;
#[link(name = "Foundation", kind = "framework")]
unsafe extern "C" {}
#[inline(always)]
pub fn inline_get_object_class() -> objc::Class {
objc::class!("NSObject")
}
#[inline(always)]
pub fn inline_get_alloc_selector() -> objc::SEL {
objc::selector!("alloc")
}
#[inline(never)]
pub fn never_inline_get_string_class() -> objc::Class {
objc::class!("NSString")
}
#[inline(never)]
pub fn never_inline_get_init_selector() -> objc::SEL {
objc::selector!("init")
}

View File

@@ -0,0 +1,52 @@
// Test that we don't generate Objective-C definitions or image info unnecessarily.
//@ add-core-stubs
//@ revisions: i686_apple_darwin
//@ [i686_apple_darwin] compile-flags: --target i686-apple-darwin
//@ [i686_apple_darwin] needs-llvm-components: x86
//@ revisions: x86_64_macos
//@ [x86_64_macos] compile-flags: --target x86_64-apple-darwin
//@ [x86_64_macos] needs-llvm-components: x86
//@ revisions: aarch64_macos
//@ [aarch64_macos] compile-flags: --target aarch64-apple-darwin
//@ [aarch64_macos] needs-llvm-components: aarch64
//@ revisions: i386_ios
//@ [i386_ios] compile-flags: --target i386-apple-ios
//@ [i386_ios] needs-llvm-components: x86
//@ revisions: x86_64_ios
//@ [x86_64_ios] compile-flags: --target x86_64-apple-ios
//@ [x86_64_ios] needs-llvm-components: x86
//@ revisions: armv7s_ios
//@ [armv7s_ios] compile-flags: --target armv7s-apple-ios
//@ [armv7s_ios] needs-llvm-components: arm
//@ revisions: aarch64_ios
//@ [aarch64_ios] compile-flags: --target aarch64-apple-ios
//@ [aarch64_ios] needs-llvm-components: aarch64
//@ revisions: aarch64_ios_sim
//@ [aarch64_ios_sim] compile-flags: --target aarch64-apple-ios-sim
//@ [aarch64_ios_sim] needs-llvm-components: aarch64
#![crate_type = "lib"]
#![feature(no_core, lang_items)]
#![no_core]
extern crate minicore;
use minicore::*;
#[no_mangle]
pub fn foo() {}
// CHECK-NOT: %struct._class_t
// CHECK-NOT: %struct._objc_module
// CHECK-NOT: @OBJC_CLASS_NAME_
// CHECK-NOT: @"OBJC_CLASS_$_{{[0-9A-Z_a-z]+}}"
// CHECK-NOT: @"OBJC_CLASSLIST_REFERENCES_$_.{{[0-9]+}}"
// CHECK-NOT: @OBJC_METH_VAR_NAME_
// CHECK-NOT: @OBJC_SELECTOR_REFERENCES_
// CHECK-NOT: @OBJC_MODULES
// CHECK-NOT: !"Objective-C Version"
// CHECK-NOT: !"Objective-C Image Info Version"
// CHECK-NOT: !"Objective-C Image Info Section"
// CHECK-NOT: !"Objective-C Is Simulated"
// CHECK-NOT: !"Objective-C Class Properties"

View File

@@ -0,0 +1,100 @@
// ignore-tidy-linelength
//@ add-core-stubs
//@ revisions: i686_apple_darwin
//@ [i686_apple_darwin] compile-flags: --target i686-apple-darwin
//@ [i686_apple_darwin] needs-llvm-components: x86
#![crate_type = "lib"]
#![feature(no_core, lang_items, rustc_attrs)]
#![no_core]
extern crate minicore;
use minicore::*;
#[no_mangle]
pub fn get_class() -> *mut () {
unsafe extern "C" {
#[rustc_objc_class = "MyClass"]
safe static VAL: *mut ();
}
VAL
}
#[no_mangle]
pub fn get_class_again() -> *mut () {
// Codegen should de-duplicate this class with the one from get_class above.
unsafe extern "C" {
#[rustc_objc_class = "MyClass"]
safe static VAL: *mut ();
}
VAL
}
#[no_mangle]
pub fn get_selector() -> *mut () {
unsafe extern "C" {
#[rustc_objc_selector = "myMethod"]
safe static VAL: *mut ();
}
VAL
}
#[no_mangle]
pub fn get_selector_again() -> *mut () {
// Codegen should de-duplicate this selector with the one from get_selector above.
unsafe extern "C" {
#[rustc_objc_selector = "myMethod"]
safe static VAL: *mut ();
}
VAL
}
#[no_mangle]
pub fn get_other_class() -> *mut () {
unsafe extern "C" {
#[rustc_objc_class = "OtherClass"]
safe static VAL: *mut ();
}
VAL
}
#[no_mangle]
pub fn get_other_selector() -> *mut () {
unsafe extern "C" {
#[rustc_objc_selector = "otherMethod"]
safe static VAL: *mut ();
}
VAL
}
// CHECK: %struct._objc_module = type { i32, i32, ptr, ptr }
// CHECK: @OBJC_CLASS_NAME_.{{[0-9]+}} = private unnamed_addr constant [8 x i8] c"MyClass\00", section "__TEXT,__cstring,cstring_literals", align 1
// CHECK: @OBJC_CLASS_REFERENCES_.{{[0-9]+}} = private global ptr @OBJC_CLASS_NAME_.{{[0-9]+}}, section "__OBJC,__cls_refs,literal_pointers,no_dead_strip", align 4
// CHECK-NOT: @OBJC_CLASS_NAME_
// CHECK-NOT: @OBJC_CLASS_REFERENCES_
// CHECK: @OBJC_METH_VAR_NAME_.{{[0-9]+}} = private unnamed_addr constant [9 x i8] c"myMethod\00", section "__TEXT,__cstring,cstring_literals", align 1
// CHECK: @OBJC_SELECTOR_REFERENCES_.{{[0-9]+}} = private externally_initialized global ptr @OBJC_METH_VAR_NAME_.{{[0-9]+}}, section "__OBJC,__message_refs,literal_pointers,no_dead_strip", align 4
// CHECK-NOT: @OBJC_METH_VAR_NAME_
// CHECK-NOT: @OBJC_SELECTOR_REFERENCES_
// CHECK: @OBJC_CLASS_NAME_.{{[0-9]+}} = private unnamed_addr constant [11 x i8] c"OtherClass\00", section "__TEXT,__cstring,cstring_literals", align 1
// CHECK: @OBJC_CLASS_REFERENCES_.{{[0-9]+}} = private global ptr @OBJC_CLASS_NAME_.{{[0-9]+}}, section "__OBJC,__cls_refs,literal_pointers,no_dead_strip", align 4
// CHECK: @OBJC_METH_VAR_NAME_.{{[0-9]+}} = private unnamed_addr constant [12 x i8] c"otherMethod\00", section "__TEXT,__cstring,cstring_literals", align 1
// CHECK: @OBJC_SELECTOR_REFERENCES_.{{[0-9]+}} = private externally_initialized global ptr @OBJC_METH_VAR_NAME_.{{[0-9]+}}, section "__OBJC,__message_refs,literal_pointers,no_dead_strip", align 4
// CHECK: @OBJC_CLASS_NAME_.{{[0-9]+}} = private unnamed_addr constant [1 x i8] zeroinitializer, section "__TEXT,__cstring,cstring_literals", align 1
// CHECK: @OBJC_MODULES = private global %struct._objc_module { i32 7, i32 16, ptr @OBJC_CLASS_NAME_.{{[0-9]+}}, ptr null }, section "__OBJC,__module_info,regular,no_dead_strip", align 4
// CHECK: load ptr, ptr @OBJC_CLASS_REFERENCES_.{{[0-9]+}}, align 4
// CHECK: load ptr, ptr @OBJC_SELECTOR_REFERENCES_.{{[0-9]+}}, align 4
// CHECK: load ptr, ptr @OBJC_CLASS_REFERENCES_.{{[0-9]+}}, align 4
// CHECK: load ptr, ptr @OBJC_SELECTOR_REFERENCES_.{{[0-9]+}}, align 4
// CHECK: !{{[0-9]+}} = !{i32 1, !"Objective-C Version", i32 1}
// CHECK: !{{[0-9]+}} = !{i32 1, !"Objective-C Image Info Version", i32 0}
// CHECK: !{{[0-9]+}} = !{i32 1, !"Objective-C Image Info Section", !"__OBJC,__image_info,regular"}
// CHECK-NOT: !{{[0-9]+}} = !{i32 1, !"Objective-C Is Simulated", i32 32}
// CHECK: !{{[0-9]+}} = !{i32 1, !"Objective-C Class Properties", i32 64}

View File

@@ -0,0 +1,185 @@
// ignore-tidy-linelength
//@ add-core-stubs
//@ revisions: x86_64_macos
//@ [x86_64_macos] compile-flags: --target x86_64-apple-darwin
//@ [x86_64_macos] needs-llvm-components: x86
//@ revisions: aarch64_macos
//@ [aarch64_macos] compile-flags: --target aarch64-apple-darwin
//@ [aarch64_macos] needs-llvm-components: aarch64
//@ revisions: i386_ios
//@ [i386_ios] compile-flags: --target i386-apple-ios
//@ [i386_ios] needs-llvm-components: x86
//@ revisions: x86_64_ios
//@ [x86_64_ios] compile-flags: --target x86_64-apple-ios
//@ [x86_64_ios] needs-llvm-components: x86
//@ revisions: armv7s_ios
//@ [armv7s_ios] compile-flags: --target armv7s-apple-ios
//@ [armv7s_ios] needs-llvm-components: arm
//@ revisions: aarch64_ios
//@ [aarch64_ios] compile-flags: --target aarch64-apple-ios
//@ [aarch64_ios] needs-llvm-components: aarch64
//@ revisions: aarch64_ios_sim
//@ [aarch64_ios_sim] compile-flags: --target aarch64-apple-ios-sim
//@ [aarch64_ios_sim] needs-llvm-components: aarch64
#![crate_type = "lib"]
#![feature(no_core, lang_items, rustc_attrs)]
#![no_core]
extern crate minicore;
use minicore::*;
#[no_mangle]
pub fn get_class() -> *mut () {
unsafe extern "C" {
#[rustc_objc_class = "MyClass"]
safe static VAL: *mut ();
}
VAL
}
#[no_mangle]
pub fn get_class_again() -> *mut () {
// Codegen should de-duplicate this class with the one from get_class above.
unsafe extern "C" {
#[rustc_objc_class = "MyClass"]
safe static VAL: *mut ();
}
VAL
}
#[no_mangle]
pub fn get_selector() -> *mut () {
unsafe extern "C" {
#[rustc_objc_selector = "myMethod"]
safe static VAL: *mut ();
}
VAL
}
#[no_mangle]
pub fn get_selector_again() -> *mut () {
// Codegen should de-duplicate this selector with the one from get_selector above.
unsafe extern "C" {
#[rustc_objc_selector = "myMethod"]
safe static VAL: *mut ();
}
VAL
}
#[no_mangle]
pub fn get_other_class() -> *mut () {
unsafe extern "C" {
#[rustc_objc_class = "OtherClass"]
safe static VAL: *mut ();
}
VAL
}
#[no_mangle]
pub fn get_other_selector() -> *mut () {
unsafe extern "C" {
#[rustc_objc_selector = "otherMethod"]
safe static VAL: *mut ();
}
VAL
}
// CHECK: %struct._class_t = type { ptr, ptr, ptr, ptr, ptr }
// CHECK: @"OBJC_CLASS_$_MyClass" = external global %struct._class_t
// CHECK: @"OBJC_CLASSLIST_REFERENCES_$_.{{[0-9]+}}" = internal global ptr @"OBJC_CLASS_$_MyClass", section "__DATA,__objc_classrefs,regular,no_dead_strip",
// x86_64_macos-SAME: align 8
// aarch64_macos-SAME: align 8
// i386_ios-SAME: align 4
// x86_64_ios-SAME: align 8
// armv7s_ios-SAME: align 4
// aarch64_ios-SAME: align 8
// aarch64_ios_sim-SAME: align 8
// CHECK-NOT: @"OBJC_CLASS_$_MyClass"
// CHECK-NOT: @"OBJC_CLASSLIST_REFERENCES_$_
// CHECK: @OBJC_METH_VAR_NAME_.{{[0-9]+}} = private unnamed_addr constant [9 x i8] c"myMethod\00", section "__TEXT,__objc_methname,cstring_literals", align 1
// CHECK: @OBJC_SELECTOR_REFERENCES_.{{[0-9]+}} = internal externally_initialized global ptr @OBJC_METH_VAR_NAME_.{{[0-9]+}}, section "__DATA,__objc_selrefs,literal_pointers,no_dead_strip",
// x86_64_macos-SAME: align 8
// aarch64_macos-SAME: align 8
// i386_ios-SAME: align 4
// x86_64_ios-SAME: align 8
// armv7s_ios-SAME: align 4
// aarch64_ios-SAME: align 8
// aarch64_ios_sim-SAME: align 8
// CHECK-NOT: @OBJC_METH_VAR_NAME_
// CHECK-NOT: @OBJC_SELECTOR_REFERENCES_
// CHECK: @"OBJC_CLASS_$_OtherClass" = external global %struct._class_t
// CHECK: @"OBJC_CLASSLIST_REFERENCES_$_.{{[0-9]+}}" = internal global ptr @"OBJC_CLASS_$_OtherClass", section "__DATA,__objc_classrefs,regular,no_dead_strip",
// x86_64_macos-SAME: align 8
// aarch64_macos-SAME: align 8
// i386_ios-SAME: align 4
// x86_64_ios-SAME: align 8
// armv7s_ios-SAME: align 4
// aarch64_ios-SAME: align 8
// aarch64_ios_sim-SAME: align 8
// CHECK: @OBJC_METH_VAR_NAME_.{{[0-9]+}} = private unnamed_addr constant [12 x i8] c"otherMethod\00", section "__TEXT,__objc_methname,cstring_literals", align 1
// CHECK: @OBJC_SELECTOR_REFERENCES_.{{[0-9]+}} = internal externally_initialized global ptr @OBJC_METH_VAR_NAME_.{{[0-9]+}}, section "__DATA,__objc_selrefs,literal_pointers,no_dead_strip",
// x86_64_macos-SAME: align 8
// aarch64_macos-SAME: align 8
// i386_ios-SAME: align 4
// x86_64_ios-SAME: align 8
// armv7s_ios-SAME: align 4
// aarch64_ios-SAME: align 8
// aarch64_ios_sim-SAME: align 8
// CHECK-NOT: @OBJC_CLASS_NAME_
// CHECK-NOT: @OBJC_MODULES
// CHECK: load ptr, ptr @"OBJC_CLASSLIST_REFERENCES_$_.{{[0-9]+}}",
// x86_64_macos-SAME: align 8
// aarch64_macos-SAME: align 8
// i386_ios-SAME: align 4
// x86_64_ios-SAME: align 8
// armv7s_ios-SAME: align 4
// aarch64_ios-SAME: align 8
// aarch64_ios_sim-SAME: align 8
// CHECK: load ptr, ptr @OBJC_SELECTOR_REFERENCES_.{{[0-9]+}},
// x86_64_macos-SAME: align 8
// aarch64_macos-SAME: align 8
// i386_ios-SAME: align 4
// x86_64_ios-SAME: align 8
// armv7s_ios-SAME: align 4
// aarch64_ios-SAME: align 8
// aarch64_ios_sim-SAME: align 8
// CHECK: load ptr, ptr @"OBJC_CLASSLIST_REFERENCES_$_.{{[0-9]+}}",
// x86_64_macos-SAME: align 8
// aarch64_macos-SAME: align 8
// i386_ios-SAME: align 4
// x86_64_ios-SAME: align 8
// armv7s_ios-SAME: align 4
// aarch64_ios-SAME: align 8
// aarch64_ios_sim-SAME: align 8
// CHECK: load ptr, ptr @OBJC_SELECTOR_REFERENCES_.{{[0-9]+}},
// x86_64_macos-SAME: align 8
// aarch64_macos-SAME: align 8
// i386_ios-SAME: align 4
// x86_64_ios-SAME: align 8
// armv7s_ios-SAME: align 4
// aarch64_ios-SAME: align 8
// aarch64_ios_sim-SAME: align 8
// CHECK: !{{[0-9]+}} = !{i32 1, !"Objective-C Version", i32 2}
// CHECK: !{{[0-9]+}} = !{i32 1, !"Objective-C Image Info Version", i32 0}
// CHECK: !{{[0-9]+}} = !{i32 1, !"Objective-C Image Info Section", !"__DATA,__objc_imageinfo,regular,no_dead_strip"}
// x86_64_macos-NOT: !{{[0-9]+}} = !{i32 1, !"Objective-C Is Simulated", i32 32}
// aarch64_macos-NOT: !{{[0-9]+}} = !{i32 1, !"Objective-C Is Simulated", i32 32}
// i386_ios: !{{[0-9]+}} = !{i32 1, !"Objective-C Is Simulated", i32 32}
// x86_64_ios: !{{[0-9]+}} = !{i32 1, !"Objective-C Is Simulated", i32 32}
// armv7s_ios-NOT: !{{[0-9]+}} = !{i32 1, !"Objective-C Is Simulated", i32 32}
// aarch64_ios-NOT: !{{[0-9]+}} = !{i32 1, !"Objective-C Is Simulated", i32 32}
// aarch64_ios_sim: !{{[0-9]+}} = !{i32 1, !"Objective-C Is Simulated", i32 32}
// CHECK: !{{[0-9]+}} = !{i32 1, !"Objective-C Class Properties", i32 64}

View File

@@ -0,0 +1,58 @@
// Test that Objective-C class and selector references inlined across crates
// get defined in this CGU but non-inline references don't.
// ignore-tidy-linelength
//@ aux-build: darwin_objc_aux.rs
//@ revisions: x86_64_macos aarch64_macos
//@ [x86_64_macos] only-x86_64-apple-darwin
//@ [aarch64_macos] only-aarch64-apple-darwin
#![crate_type = "lib"]
#![feature(darwin_objc)]
use std::os::darwin::objc;
extern crate darwin_objc_aux as aux;
#[no_mangle]
pub fn get_object_class() -> objc::Class {
aux::inline_get_object_class()
}
#[no_mangle]
pub fn get_alloc_selector() -> objc::SEL {
aux::inline_get_alloc_selector()
}
#[no_mangle]
pub fn get_string_class() -> objc::Class {
aux::never_inline_get_string_class()
}
#[no_mangle]
pub fn get_init_selector() -> objc::SEL {
aux::never_inline_get_init_selector()
}
// CHECK: %struct._class_t = type { ptr, ptr, ptr, ptr, ptr }
// CHECK: @"OBJC_CLASS_$_NSObject" = external global %struct._class_t
// CHECK: @"OBJC_CLASSLIST_REFERENCES_$_.{{[0-9]+}}" = internal global ptr @"OBJC_CLASS_$_NSObject", section "__DATA,__objc_classrefs,regular,no_dead_strip", align 8
// CHECK: @OBJC_METH_VAR_NAME_.{{[0-9]+}} = private unnamed_addr constant [6 x i8] c"alloc\00", section "__TEXT,__objc_methname,cstring_literals", align 1
// CHECK: @OBJC_SELECTOR_REFERENCES_.{{[0-9]+}} = internal externally_initialized global ptr @OBJC_METH_VAR_NAME_.{{[0-9]+}}, section "__DATA,__objc_selrefs,literal_pointers,no_dead_strip", align 8
// CHECK-NOT: @"OBJC_CLASS_$_NSString" = external global %struct._class_t
// CHECK-NOT: @"OBJC_CLASSLIST_REFERENCES_$_.{{[0-9]+}}" = internal global ptr @"OBJC_CLASS_$_NSString"
// CHECK-NOT: @OBJC_METH_VAR_NAME_.{{[0-9]+}} = private unnamed_addr constant [5 x i8] c"init\00"
// CHECK-NOT: @OBJC_SELECTOR_REFERENCES_.{{[0-9]+}} = internal externally_initialized global ptr @OBJC_METH_VAR_NAME_.{{[0-9]+}}
// CHECK: load ptr, ptr @"OBJC_CLASSLIST_REFERENCES_$_.{{[0-9]+}}", align 8
// CHECK: load ptr, ptr @OBJC_SELECTOR_REFERENCES_.{{[0-9]+}}, align 8
// CHECK: !{{[0-9]+}} = !{i32 1, !"Objective-C Version", i32 2}
// CHECK: !{{[0-9]+}} = !{i32 1, !"Objective-C Image Info Version", i32 0}
// CHECK: !{{[0-9]+}} = !{i32 1, !"Objective-C Image Info Section", !"__DATA,__objc_imageinfo,regular,no_dead_strip"}
// CHECK-NOT: !{{[0-9]+}} = !{i32 1, !"Objective-C Is Simulated", i32 32}
// CHECK: !{{[0-9]+}} = !{i32 1, !"Objective-C Class Properties", i32 64}

View File

@@ -350,6 +350,12 @@ Tests for FFI with C varargs (`va_list`).
Tests for detection and handling of cyclic trait dependencies.
## `tests/ui/darwin-objc/`: Darwin Objective-C
Tests exercising `#![feature(darwin_objc)]`.
See [Tracking Issue for `darwin_objc` #145496](https://github.com/rust-lang/rust/issues/145496).
## `tests/ui/dataflow_const_prop/`
Contains a single regression test for const prop in `SwitchInt` pass crashing when `ptr2int` transmute is involved.

View File

@@ -0,0 +1,36 @@
// Test that `objc::class!` and `objc::selector!` only take string literals.
//@ edition: 2024
//@ only-apple
#![feature(darwin_objc)]
use std::os::darwin::objc;
pub fn main() {
let s = "NSObject";
objc::class!(s);
//~^ ERROR attribute value must be a literal
objc::class!(NSObject);
//~^ ERROR attribute value must be a literal
objc::class!(123);
//~^ ERROR `objc::class!` expected a string literal
objc::class!("NSObject\0");
//~^ ERROR `objc::class!` may not contain null characters
let s = "alloc";
objc::selector!(s);
//~^ ERROR attribute value must be a literal
objc::selector!(alloc);
//~^ ERROR attribute value must be a literal
objc::selector!(123);
//~^ ERROR `objc::selector!` expected a string literal
objc::selector!("alloc\0");
//~^ ERROR `objc::selector!` may not contain null characters
}

View File

@@ -0,0 +1,50 @@
error: attribute value must be a literal
--> $DIR/darwin-objc-bad-arg.rs:12:18
|
LL | objc::class!(s);
| ^
error: attribute value must be a literal
--> $DIR/darwin-objc-bad-arg.rs:15:18
|
LL | objc::class!(NSObject);
| ^^^^^^^^
error: `objc::class!` expected a string literal
--> $DIR/darwin-objc-bad-arg.rs:18:18
|
LL | objc::class!(123);
| ^^^
error: `objc::class!` may not contain null characters
--> $DIR/darwin-objc-bad-arg.rs:21:18
|
LL | objc::class!("NSObject\0");
| ^^^^^^^^^^^^
error: attribute value must be a literal
--> $DIR/darwin-objc-bad-arg.rs:25:21
|
LL | objc::selector!(s);
| ^
error: attribute value must be a literal
--> $DIR/darwin-objc-bad-arg.rs:28:21
|
LL | objc::selector!(alloc);
| ^^^^^
error: `objc::selector!` expected a string literal
--> $DIR/darwin-objc-bad-arg.rs:31:21
|
LL | objc::selector!(123);
| ^^^
error: `objc::selector!` may not contain null characters
--> $DIR/darwin-objc-bad-arg.rs:34:21
|
LL | objc::selector!("alloc\0");
| ^^^^^^^^^
error: aborting due to 8 previous errors

View File

@@ -0,0 +1,17 @@
// Test that `objc::class!` and `objc::selector!` aren't `const` expressions.
// The system gives them their final values at dynamic load time.
//@ edition: 2024
//@ only-apple
#![feature(darwin_objc)]
use std::os::darwin::objc;
pub const CLASS: objc::Class = objc::class!("NSObject");
//~^ ERROR cannot access extern static `CLASS::VAL` [E0080]
pub const SELECTOR: objc::SEL = objc::selector!("alloc");
//~^ ERROR cannot access extern static `SELECTOR::VAL` [E0080]
pub fn main() {}

View File

@@ -0,0 +1,19 @@
error[E0080]: cannot access extern static `CLASS::VAL`
--> $DIR/darwin-objc-bad-const.rs:11:32
|
LL | pub const CLASS: objc::Class = objc::class!("NSObject");
| ^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `CLASS` failed here
|
= note: this error originates in the macro `objc::class` (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0080]: cannot access extern static `SELECTOR::VAL`
--> $DIR/darwin-objc-bad-const.rs:14:33
|
LL | pub const SELECTOR: objc::SEL = objc::selector!("alloc");
| ^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `SELECTOR` failed here
|
= note: this error originates in the macro `objc::selector` (in Nightly builds, run with -Z macro-backtrace for more info)
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0080`.

View File

@@ -0,0 +1,31 @@
// Test that `objc::class!` and `objc::selector!` can't be returned by reference.
// A single instance may have multiple addresses (e.g. across dylib boundaries).
//@ edition: 2024
//@ only-apple
#![feature(darwin_objc)]
use std::os::darwin::objc;
pub fn class_ref<'a>() -> &'a objc::Class {
&objc::class!("NSObject")
//~^ ERROR cannot return reference to temporary value [E0515]
}
pub fn class_ref_static() -> &'static objc::Class {
&objc::class!("NSObject")
//~^ ERROR cannot return reference to temporary value [E0515]
}
pub fn selector_ref<'a>() -> &'a objc::SEL {
&objc::selector!("alloc")
//~^ ERROR cannot return reference to temporary value [E0515]
}
pub fn selector_ref_static() -> &'static objc::SEL {
&objc::selector!("alloc")
//~^ ERROR cannot return reference to temporary value [E0515]
}
pub fn main() {}

View File

@@ -0,0 +1,39 @@
error[E0515]: cannot return reference to temporary value
--> $DIR/darwin-objc-bad-ref.rs:12:5
|
LL | &objc::class!("NSObject")
| ^------------------------
| ||
| |temporary value created here
| returns a reference to data owned by the current function
error[E0515]: cannot return reference to temporary value
--> $DIR/darwin-objc-bad-ref.rs:17:5
|
LL | &objc::class!("NSObject")
| ^------------------------
| ||
| |temporary value created here
| returns a reference to data owned by the current function
error[E0515]: cannot return reference to temporary value
--> $DIR/darwin-objc-bad-ref.rs:22:5
|
LL | &objc::selector!("alloc")
| ^------------------------
| ||
| |temporary value created here
| returns a reference to data owned by the current function
error[E0515]: cannot return reference to temporary value
--> $DIR/darwin-objc-bad-ref.rs:27:5
|
LL | &objc::selector!("alloc")
| ^------------------------
| ||
| |temporary value created here
| returns a reference to data owned by the current function
error: aborting due to 4 previous errors
For more information about this error, try `rustc --explain E0515`.

View File

@@ -0,0 +1,31 @@
// Call `[NSObject class]` using `objc::class!` and `objc::selector!`.
//@ edition: 2024
//@ only-apple
//@ run-pass
#![feature(darwin_objc)]
use std::mem::transmute;
use std::os::darwin::objc;
#[link(name = "Foundation", kind = "framework")]
unsafe extern "C" {}
#[link(name = "objc", kind = "dylib")]
unsafe extern "C" {
unsafe fn objc_msgSend();
}
fn main() {
let msg_send_fn = unsafe {
transmute::<
unsafe extern "C" fn(),
unsafe extern "C" fn(objc::Class, objc::SEL) -> objc::Class,
>(objc_msgSend)
};
let static_sel = objc::selector!("class");
let static_class = objc::class!("NSObject");
let runtime_class = unsafe { msg_send_fn(static_class, static_sel) };
assert_eq!(static_class, runtime_class);
}

View File

@@ -0,0 +1,39 @@
// Test that `objc::class!` returns the same thing as `objc_lookUpClass`.
//@ edition: 2024
//@ only-apple
//@ run-pass
#![feature(darwin_objc)]
use std::ffi::c_char;
use std::os::darwin::objc;
#[link(name = "Foundation", kind = "framework")]
unsafe extern "C" {}
#[link(name = "objc")]
unsafe extern "C" {
fn objc_lookUpClass(methname: *const c_char) -> objc::Class;
}
fn get_object_class() -> objc::Class {
objc::class!("NSObject")
}
fn lookup_object_class() -> objc::Class {
unsafe { objc_lookUpClass(c"NSObject".as_ptr()) }
}
fn get_string_class() -> objc::Class {
objc::class!("NSString")
}
fn lookup_string_class() -> objc::Class {
unsafe { objc_lookUpClass(c"NSString".as_ptr()) }
}
fn main() {
assert_eq!(get_object_class(), lookup_object_class());
assert_eq!(get_string_class(), lookup_string_class());
}

View File

@@ -0,0 +1,36 @@
// Test that `objc::selector!` returns the same thing as `sel_registerName`.
//@ edition: 2024
//@ only-apple
//@ run-pass
#![feature(darwin_objc)]
use std::ffi::c_char;
use std::os::darwin::objc;
#[link(name = "objc")]
unsafe extern "C" {
fn sel_registerName(methname: *const c_char) -> objc::SEL;
}
fn get_alloc_selector() -> objc::SEL {
objc::selector!("alloc")
}
fn register_alloc_selector() -> objc::SEL {
unsafe { sel_registerName(c"alloc".as_ptr()) }
}
fn get_init_selector() -> objc::SEL {
objc::selector!("initWithCString:encoding:")
}
fn register_init_selector() -> objc::SEL {
unsafe { sel_registerName(c"initWithCString:encoding:".as_ptr()) }
}
fn main() {
assert_eq!(get_alloc_selector(), register_alloc_selector());
assert_eq!(get_init_selector(), register_init_selector());
}