Skip to content

Commit c6e490c

Browse files
committed
initial implementation of the darwin_objc unstable feature
1 parent 93edf9f commit c6e490c

File tree

23 files changed

+706
-11
lines changed

23 files changed

+706
-11
lines changed

compiler/rustc_attr_parsing/messages.ftl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,10 @@ attr_parsing_null_on_export = `export_name` may not contain null characters
116116
117117
attr_parsing_null_on_link_section = `link_section` may not contain null characters
118118
119+
attr_parsing_null_on_objc_class = `rustc_objc_class` may not contain null characters
120+
121+
attr_parsing_null_on_objc_selector = `rustc_objc_selector` may not contain null characters
122+
119123
attr_parsing_repr_ident =
120124
meta item in `repr` must be an identifier
121125

compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ use rustc_hir::attrs::{CoverageAttrKind, OptimizeAttr, SanitizerSet, UsedBy};
22
use rustc_session::parse::feature_err;
33

44
use super::prelude::*;
5-
use crate::session_diagnostics::{NakedFunctionIncompatibleAttribute, NullOnExport};
5+
use crate::session_diagnostics::{
6+
NakedFunctionIncompatibleAttribute, NullOnExport, NullOnObjcClass, NullOnObjcSelector,
7+
};
68

79
pub(crate) struct OptimizeParser;
810

@@ -149,6 +151,64 @@ impl<S: Stage> SingleAttributeParser<S> for ExportNameParser {
149151
}
150152
}
151153

154+
pub(crate) struct ObjcClassParser;
155+
156+
impl<S: Stage> SingleAttributeParser<S> for ObjcClassParser {
157+
const PATH: &[rustc_span::Symbol] = &[sym::rustc_objc_class];
158+
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost;
159+
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
160+
const ALLOWED_TARGETS: AllowedTargets =
161+
AllowedTargets::AllowList(&[Allow(Target::ForeignStatic)]);
162+
const TEMPLATE: AttributeTemplate = template!(NameValueStr: "ClassName");
163+
164+
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
165+
let Some(nv) = args.name_value() else {
166+
cx.expected_name_value(cx.attr_span, None);
167+
return None;
168+
};
169+
let Some(classname) = nv.value_as_str() else {
170+
cx.expected_string_literal(nv.value_span, Some(nv.value_as_lit()));
171+
return None;
172+
};
173+
if classname.as_str().contains('\0') {
174+
// `#[rustc_objc_class = ...]` will be converted to a null-terminated string,
175+
// so it may not contain any null characters.
176+
cx.emit_err(NullOnObjcClass { span: cx.attr_span });
177+
return None;
178+
}
179+
Some(AttributeKind::ObjcClass { classname, span: cx.attr_span })
180+
}
181+
}
182+
183+
pub(crate) struct ObjcSelectorParser;
184+
185+
impl<S: Stage> SingleAttributeParser<S> for ObjcSelectorParser {
186+
const PATH: &[rustc_span::Symbol] = &[sym::rustc_objc_selector];
187+
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost;
188+
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
189+
const ALLOWED_TARGETS: AllowedTargets =
190+
AllowedTargets::AllowList(&[Allow(Target::ForeignStatic)]);
191+
const TEMPLATE: AttributeTemplate = template!(NameValueStr: "methodName");
192+
193+
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
194+
let Some(nv) = args.name_value() else {
195+
cx.expected_name_value(cx.attr_span, None);
196+
return None;
197+
};
198+
let Some(methname) = nv.value_as_str() else {
199+
cx.expected_string_literal(nv.value_span, Some(nv.value_as_lit()));
200+
return None;
201+
};
202+
if methname.as_str().contains('\0') {
203+
// `#[rustc_objc_selector = ...]` will be converted to a null-terminated string,
204+
// so it may not contain any null characters.
205+
cx.emit_err(NullOnObjcSelector { span: cx.attr_span });
206+
return None;
207+
}
208+
Some(AttributeKind::ObjcSelector { methname, span: cx.attr_span })
209+
}
210+
}
211+
152212
#[derive(Default)]
153213
pub(crate) struct NakedParser {
154214
span: Option<Span>,

compiler/rustc_attr_parsing/src/context.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ use crate::attributes::allow_unstable::{
2020
use crate::attributes::body::CoroutineParser;
2121
use crate::attributes::codegen_attrs::{
2222
ColdParser, CoverageParser, ExportNameParser, ForceTargetFeatureParser, NakedParser,
23-
NoMangleParser, OptimizeParser, SanitizeParser, TargetFeatureParser, TrackCallerParser,
24-
UsedParser,
23+
NoMangleParser, ObjcClassParser, ObjcSelectorParser, OptimizeParser, SanitizeParser,
24+
TargetFeatureParser, TrackCallerParser, UsedParser,
2525
};
2626
use crate::attributes::confusables::ConfusablesParser;
2727
use crate::attributes::crate_level::CrateNameParser;
@@ -179,6 +179,8 @@ attribute_parsers!(
179179
Single<LinkSectionParser>,
180180
Single<LinkageParser>,
181181
Single<MustUseParser>,
182+
Single<ObjcClassParser>,
183+
Single<ObjcSelectorParser>,
182184
Single<OptimizeParser>,
183185
Single<PathAttributeParser>,
184186
Single<ProcMacroDeriveParser>,

compiler/rustc_attr_parsing/src/session_diagnostics.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -459,6 +459,20 @@ pub(crate) struct NullOnLinkSection {
459459
pub span: Span,
460460
}
461461

462+
#[derive(Diagnostic)]
463+
#[diag(attr_parsing_null_on_objc_class)]
464+
pub(crate) struct NullOnObjcClass {
465+
#[primary_span]
466+
pub span: Span,
467+
}
468+
469+
#[derive(Diagnostic)]
470+
#[diag(attr_parsing_null_on_objc_selector)]
471+
pub(crate) struct NullOnObjcSelector {
472+
#[primary_span]
473+
pub span: Span,
474+
}
475+
462476
#[derive(Diagnostic)]
463477
#[diag(attr_parsing_stability_outside_std, code = E0734)]
464478
pub(crate) struct StabilityOutsideStd {

compiler/rustc_codegen_llvm/src/base.rs

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -109,18 +109,35 @@ pub(crate) fn compile_codegen_unit(
109109
attributes::apply_to_llfn(entry, llvm::AttributePlace::Function, &attrs);
110110
}
111111

112+
// Define Objective-C module info for 32-bit x86 macOS. Note, this generates a global
113+
// that gets added to the `llvm.compiler.used` variable, created later.
114+
//
115+
// This is only necessary when we need the linker to do its Objective-C-specific magic.
116+
// We could theoretically do it unconditionally, but at a slight cost to linker
117+
// performance in the common case where it's unnecessary.
118+
let uses_objc =
119+
!cx.objc_classrefs.borrow().is_empty() || !cx.objc_selrefs.borrow().is_empty();
120+
if uses_objc && cx.tcx.sess.target.arch == "x86" && cx.tcx.sess.target.os == "macos" {
121+
cx.define_objc_module_info();
122+
}
123+
112124
// Finalize code coverage by injecting the coverage map. Note, the coverage map will
113125
// also be added to the `llvm.compiler.used` variable, created next.
114126
if cx.sess().instrument_coverage() {
115127
cx.coverageinfo_finalize();
116128
}
117129

118-
// Create the llvm.used and llvm.compiler.used variables.
130+
// Create the llvm.used variable.
119131
if !cx.used_statics.is_empty() {
120132
cx.create_used_variable_impl(c"llvm.used", &cx.used_statics);
121133
}
122-
if !cx.compiler_used_statics.is_empty() {
123-
cx.create_used_variable_impl(c"llvm.compiler.used", &cx.compiler_used_statics);
134+
135+
// Create the llvm.compiler.used variable.
136+
{
137+
let compiler_used_statics = cx.compiler_used_statics.borrow();
138+
if !compiler_used_statics.is_empty() {
139+
cx.create_used_variable_impl(c"llvm.compiler.used", &compiler_used_statics);
140+
}
124141
}
125142

126143
// Run replace-all-uses-with for statics that need it. This must
@@ -132,6 +149,15 @@ pub(crate) fn compile_codegen_unit(
132149
}
133150
}
134151

152+
// Add Objective-C module flags.
153+
//
154+
// This is only necessary when we need the linker to do its Objective-C-specific magic.
155+
// We could theoretically do it unconditionally, but at a slight cost to linker
156+
// performance in the common case where it's unnecessary.
157+
if uses_objc {
158+
cx.add_objc_module_flags();
159+
}
160+
135161
// Finalize debuginfo
136162
if cx.sess().opts.debuginfo != DebugInfo::None {
137163
cx.debuginfo_finalize();

compiler/rustc_codegen_llvm/src/common.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,10 @@ impl<'ll, CX: Borrow<SCx<'ll>>> GenericCx<'ll, CX> {
108108
bytes_in_context(self.llcx(), bytes)
109109
}
110110

111+
pub(crate) fn null_terminate_const_bytes(&self, bytes: &[u8]) -> &'ll Value {
112+
null_terminate_bytes_in_context(self.llcx(), bytes)
113+
}
114+
111115
pub(crate) fn const_get_elt(&self, v: &'ll Value, idx: u64) -> &'ll Value {
112116
unsafe {
113117
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]) -> &
381385
}
382386
}
383387

388+
pub(crate) fn null_terminate_bytes_in_context<'ll>(
389+
llcx: &'ll llvm::Context,
390+
bytes: &[u8],
391+
) -> &'ll Value {
392+
unsafe {
393+
let ptr = bytes.as_ptr() as *const c_char;
394+
llvm::LLVMConstStringInContext2(llcx, ptr, bytes.len(), False)
395+
}
396+
}
397+
384398
pub(crate) fn named_struct<'ll>(ty: &'ll Type, elts: &[&'ll Value]) -> &'ll Value {
385399
let len = c_uint::try_from(elts.len()).expect("LLVMConstStructInContext elements len overflow");
386400
unsafe { llvm::LLVMConstNamedStruct(ty, elts.as_ptr(), len) }

0 commit comments

Comments
 (0)