Skip to content

Commit 11d96b5

Browse files
committed
Auto merge of #107257 - inquisitivecrystal:ffi-attr, r=davidtwco
Strengthen validation of FFI attributes Previously, `codegen_attrs` validated the attributes `#[ffi_pure]`, `#[ffi_const]`, and `#[ffi_returns_twice]` to make sure that they were only used on foreign functions. However, this validation was insufficient in two ways: 1. `codegen_attrs` only sees items for which code must be generated, so it was unable to raise errors when the attribute was incorrectly applied to macros and the like. 2. the validation code only checked that the item with the attr was foreign, but not that it was a foreign function, allowing these attributes to be applied to foreign statics as well. This PR moves the validation to `check_attr`, which sees all items. It additionally changes the validation to ensure that the attribute's target is `Target::ForeignFunction`, only allowing the attributes on foreign functions and not foreign statics. Because these attributes are unstable, there is no risk for backwards compatibility. The changes also ending up making the code much easier to read. This PR is best reviewed commit by commit. Additionally, I was considering moving the tests to the `attribute` subdirectory, to get them out of the general UI directory. I could do that as part of this PR or a follow-up, as the reviewer prefers. CC: #58328, #58329
2 parents 3b63948 + bc23e9a commit 11d96b5

File tree

10 files changed

+147
-50
lines changed

10 files changed

+147
-50
lines changed

compiler/rustc_codegen_ssa/src/codegen_attrs.rs

+3-47
Original file line numberDiff line numberDiff line change
@@ -85,55 +85,11 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: DefId) -> CodegenFnAttrs {
8585
} else if attr.has_name(sym::rustc_allocator) {
8686
codegen_fn_attrs.flags |= CodegenFnAttrFlags::ALLOCATOR;
8787
} else if attr.has_name(sym::ffi_returns_twice) {
88-
if tcx.is_foreign_item(did) {
89-
codegen_fn_attrs.flags |= CodegenFnAttrFlags::FFI_RETURNS_TWICE;
90-
} else {
91-
// `#[ffi_returns_twice]` is only allowed `extern fn`s.
92-
struct_span_err!(
93-
tcx.sess,
94-
attr.span,
95-
E0724,
96-
"`#[ffi_returns_twice]` may only be used on foreign functions"
97-
)
98-
.emit();
99-
}
88+
codegen_fn_attrs.flags |= CodegenFnAttrFlags::FFI_RETURNS_TWICE;
10089
} else if attr.has_name(sym::ffi_pure) {
101-
if tcx.is_foreign_item(did) {
102-
if attrs.iter().any(|a| a.has_name(sym::ffi_const)) {
103-
// `#[ffi_const]` functions cannot be `#[ffi_pure]`
104-
struct_span_err!(
105-
tcx.sess,
106-
attr.span,
107-
E0757,
108-
"`#[ffi_const]` function cannot be `#[ffi_pure]`"
109-
)
110-
.emit();
111-
} else {
112-
codegen_fn_attrs.flags |= CodegenFnAttrFlags::FFI_PURE;
113-
}
114-
} else {
115-
// `#[ffi_pure]` is only allowed on foreign functions
116-
struct_span_err!(
117-
tcx.sess,
118-
attr.span,
119-
E0755,
120-
"`#[ffi_pure]` may only be used on foreign functions"
121-
)
122-
.emit();
123-
}
90+
codegen_fn_attrs.flags |= CodegenFnAttrFlags::FFI_PURE;
12491
} else if attr.has_name(sym::ffi_const) {
125-
if tcx.is_foreign_item(did) {
126-
codegen_fn_attrs.flags |= CodegenFnAttrFlags::FFI_CONST;
127-
} else {
128-
// `#[ffi_const]` is only allowed on foreign functions
129-
struct_span_err!(
130-
tcx.sess,
131-
attr.span,
132-
E0756,
133-
"`#[ffi_const]` may only be used on foreign functions"
134-
)
135-
.emit();
136-
}
92+
codegen_fn_attrs.flags |= CodegenFnAttrFlags::FFI_CONST;
13793
} else if attr.has_name(sym::rustc_nounwind) {
13894
codegen_fn_attrs.flags |= CodegenFnAttrFlags::NEVER_UNWIND;
13995
} else if attr.has_name(sym::rustc_reallocator) {

compiler/rustc_error_messages/locales/en-US/passes.ftl

+12
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,18 @@ passes_has_incoherent_inherent_impl =
182182
`rustc_has_incoherent_inherent_impls` attribute should be applied to types or traits.
183183
.label = only adts, extern types and traits are supported
184184
185+
passes_both_ffi_const_and_pure =
186+
`#[ffi_const]` function cannot be `#[ffi_pure]`
187+
188+
passes_ffi_pure_invalid_target =
189+
`#[ffi_pure]` may only be used on foreign functions
190+
191+
passes_ffi_const_invalid_target =
192+
`#[ffi_const]` may only be used on foreign functions
193+
194+
passes_ffi_returns_twice_invalid_target =
195+
`#[ffi_returns_twice]` may only be used on foreign functions
196+
185197
passes_must_use_async =
186198
`must_use` attribute on `async` functions applies to the anonymous `Future` returned by the function, not the value within
187199
.label = this attribute does nothing, the `Future`s returned by async functions are already `must_use`

compiler/rustc_passes/src/check_attr.rs

+35
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,9 @@ impl CheckAttrVisitor<'_> {
174174
sym::rustc_has_incoherent_inherent_impls => {
175175
self.check_has_incoherent_inherent_impls(&attr, span, target)
176176
}
177+
sym::ffi_pure => self.check_ffi_pure(attr.span, attrs, target),
178+
sym::ffi_const => self.check_ffi_const(attr.span, target),
179+
sym::ffi_returns_twice => self.check_ffi_returns_twice(attr.span, target),
177180
sym::rustc_const_unstable
178181
| sym::rustc_const_stable
179182
| sym::unstable
@@ -1213,6 +1216,38 @@ impl CheckAttrVisitor<'_> {
12131216
}
12141217
}
12151218

1219+
fn check_ffi_pure(&self, attr_span: Span, attrs: &[Attribute], target: Target) -> bool {
1220+
if target != Target::ForeignFn {
1221+
self.tcx.sess.emit_err(errors::FfiPureInvalidTarget { attr_span });
1222+
return false;
1223+
}
1224+
if attrs.iter().any(|a| a.has_name(sym::ffi_const)) {
1225+
// `#[ffi_const]` functions cannot be `#[ffi_pure]`
1226+
self.tcx.sess.emit_err(errors::BothFfiConstAndPure { attr_span });
1227+
false
1228+
} else {
1229+
true
1230+
}
1231+
}
1232+
1233+
fn check_ffi_const(&self, attr_span: Span, target: Target) -> bool {
1234+
if target == Target::ForeignFn {
1235+
true
1236+
} else {
1237+
self.tcx.sess.emit_err(errors::FfiConstInvalidTarget { attr_span });
1238+
false
1239+
}
1240+
}
1241+
1242+
fn check_ffi_returns_twice(&self, attr_span: Span, target: Target) -> bool {
1243+
if target == Target::ForeignFn {
1244+
true
1245+
} else {
1246+
self.tcx.sess.emit_err(errors::FfiReturnsTwiceInvalidTarget { attr_span });
1247+
false
1248+
}
1249+
}
1250+
12161251
/// Warns against some misuses of `#[must_use]`
12171252
fn check_must_use(&self, hir_id: HirId, attr: &Attribute, target: Target) -> bool {
12181253
if !matches!(

compiler/rustc_passes/src/errors.rs

+28
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,34 @@ pub struct HasIncoherentInherentImpl {
348348
pub span: Span,
349349
}
350350

351+
#[derive(Diagnostic)]
352+
#[diag(passes_both_ffi_const_and_pure, code = "E0757")]
353+
pub struct BothFfiConstAndPure {
354+
#[primary_span]
355+
pub attr_span: Span,
356+
}
357+
358+
#[derive(Diagnostic)]
359+
#[diag(passes_ffi_pure_invalid_target, code = "E0755")]
360+
pub struct FfiPureInvalidTarget {
361+
#[primary_span]
362+
pub attr_span: Span,
363+
}
364+
365+
#[derive(Diagnostic)]
366+
#[diag(passes_ffi_const_invalid_target, code = "E0756")]
367+
pub struct FfiConstInvalidTarget {
368+
#[primary_span]
369+
pub attr_span: Span,
370+
}
371+
372+
#[derive(Diagnostic)]
373+
#[diag(passes_ffi_returns_twice_invalid_target, code = "E0724")]
374+
pub struct FfiReturnsTwiceInvalidTarget {
375+
#[primary_span]
376+
pub attr_span: Span,
377+
}
378+
351379
#[derive(LintDiagnostic)]
352380
#[diag(passes_must_use_async)]
353381
pub struct MustUseAsync {

tests/ui/ffi_const.rs

+10
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,13 @@
33

44
#[ffi_const] //~ ERROR `#[ffi_const]` may only be used on foreign functions
55
pub fn foo() {}
6+
7+
#[ffi_const] //~ ERROR `#[ffi_const]` may only be used on foreign functions
8+
macro_rules! bar {
9+
() => ()
10+
}
11+
12+
extern "C" {
13+
#[ffi_const] //~ ERROR `#[ffi_const]` may only be used on foreign functions
14+
static INT: i32;
15+
}

tests/ui/ffi_const.stderr

+13-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,18 @@ error[E0756]: `#[ffi_const]` may only be used on foreign functions
44
LL | #[ffi_const]
55
| ^^^^^^^^^^^^
66

7-
error: aborting due to previous error
7+
error[E0756]: `#[ffi_const]` may only be used on foreign functions
8+
--> $DIR/ffi_const.rs:7:1
9+
|
10+
LL | #[ffi_const]
11+
| ^^^^^^^^^^^^
12+
13+
error[E0756]: `#[ffi_const]` may only be used on foreign functions
14+
--> $DIR/ffi_const.rs:13:5
15+
|
16+
LL | #[ffi_const]
17+
| ^^^^^^^^^^^^
18+
19+
error: aborting due to 3 previous errors
820

921
For more information about this error, try `rustc --explain E0756`.

tests/ui/ffi_pure.rs

+10
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,13 @@
33

44
#[ffi_pure] //~ ERROR `#[ffi_pure]` may only be used on foreign functions
55
pub fn foo() {}
6+
7+
#[ffi_pure] //~ ERROR `#[ffi_pure]` may only be used on foreign functions
8+
macro_rules! bar {
9+
() => ()
10+
}
11+
12+
extern "C" {
13+
#[ffi_pure] //~ ERROR `#[ffi_pure]` may only be used on foreign functions
14+
static INT: i32;
15+
}

tests/ui/ffi_pure.stderr

+13-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,18 @@ error[E0755]: `#[ffi_pure]` may only be used on foreign functions
44
LL | #[ffi_pure]
55
| ^^^^^^^^^^^
66

7-
error: aborting due to previous error
7+
error[E0755]: `#[ffi_pure]` may only be used on foreign functions
8+
--> $DIR/ffi_pure.rs:7:1
9+
|
10+
LL | #[ffi_pure]
11+
| ^^^^^^^^^^^
12+
13+
error[E0755]: `#[ffi_pure]` may only be used on foreign functions
14+
--> $DIR/ffi_pure.rs:13:5
15+
|
16+
LL | #[ffi_pure]
17+
| ^^^^^^^^^^^
18+
19+
error: aborting due to 3 previous errors
820

921
For more information about this error, try `rustc --explain E0755`.

tests/ui/ffi_returns_twice.rs

+10
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,13 @@
33

44
#[ffi_returns_twice] //~ ERROR `#[ffi_returns_twice]` may only be used on foreign functions
55
pub fn foo() {}
6+
7+
#[ffi_returns_twice] //~ ERROR `#[ffi_returns_twice]` may only be used on foreign functions
8+
macro_rules! bar {
9+
() => ()
10+
}
11+
12+
extern "C" {
13+
#[ffi_returns_twice] //~ ERROR `#[ffi_returns_twice]` may only be used on foreign functions
14+
static INT: i32;
15+
}

tests/ui/ffi_returns_twice.stderr

+13-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,18 @@ error[E0724]: `#[ffi_returns_twice]` may only be used on foreign functions
44
LL | #[ffi_returns_twice]
55
| ^^^^^^^^^^^^^^^^^^^^
66

7-
error: aborting due to previous error
7+
error[E0724]: `#[ffi_returns_twice]` may only be used on foreign functions
8+
--> $DIR/ffi_returns_twice.rs:7:1
9+
|
10+
LL | #[ffi_returns_twice]
11+
| ^^^^^^^^^^^^^^^^^^^^
12+
13+
error[E0724]: `#[ffi_returns_twice]` may only be used on foreign functions
14+
--> $DIR/ffi_returns_twice.rs:13:5
15+
|
16+
LL | #[ffi_returns_twice]
17+
| ^^^^^^^^^^^^^^^^^^^^
18+
19+
error: aborting due to 3 previous errors
820

921
For more information about this error, try `rustc --explain E0724`.

0 commit comments

Comments
 (0)