Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit aed14e4

Browse files
committedMar 17, 2025
add FCW to warn about wasm ABI transition
1 parent 10bcdad commit aed14e4

File tree

7 files changed

+223
-13
lines changed

7 files changed

+223
-13
lines changed
 

‎compiler/rustc_lint_defs/src/builtin.rs

+46
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@ declare_lint_pass! {
143143
UNUSED_VARIABLES,
144144
USELESS_DEPRECATED,
145145
WARNINGS,
146+
WASM_C_ABI,
146147
// tidy-alphabetical-end
147148
]
148149
}
@@ -5082,6 +5083,8 @@ declare_lint! {
50825083
/// }
50835084
/// ```
50845085
///
5086+
/// This will produce:
5087+
///
50855088
/// ```text
50865089
/// warning: ABI error: this function call uses a avx vector type, which is not enabled in the caller
50875090
/// --> lint_example.rs:18:12
@@ -5125,3 +5128,46 @@ declare_lint! {
51255128
reference: "issue #116558 <https://github.com/rust-lang/rust/issues/116558>",
51265129
};
51275130
}
5131+
5132+
declare_lint! {
5133+
/// The `wasm_c_abi` lint detects usage of the `extern "C"` ABI of wasm that is affected
5134+
/// by a planned ABI change that has the goal of aligning Rust with the standard C ABI
5135+
/// of this target.
5136+
///
5137+
/// ### Example
5138+
///
5139+
/// ```rust,ignore (needs wasm32-unknown-unknown)
5140+
/// #[repr(C)]
5141+
/// struct MyType(i32, i32);
5142+
///
5143+
/// extern "C" my_fun(x: MyType) {}
5144+
/// ```
5145+
///
5146+
/// This will produce:
5147+
///
5148+
/// ```text
5149+
/// error: this function function definition is affected by the wasm ABI transition: it passes an argument of non-scalar type `MyType`
5150+
/// --> $DIR/wasm_c_abi_transition.rs:17:1
5151+
/// |
5152+
/// | pub extern "C" fn my_fun(_x: MyType) {}
5153+
/// | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
5154+
/// |
5155+
/// = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
5156+
/// = note: for more information, see issue #122532 <https://github.com/rust-lang/rust/issues/122532>
5157+
/// = help: the "C" ABI Rust uses on wasm32-unknown-unknown will change to align with the standard "C" ABI for this target
5158+
/// ```
5159+
///
5160+
/// ### Explanation
5161+
///
5162+
/// Rust has historically implemented a non-spec-compliant C ABI on wasm32-unknown-unknown. This
5163+
/// has caused incompatibilities with other compilers and Wasm targets. In a future version
5164+
/// of Rust, this will be fixed, and therefore code relying on the non-spec-compliant C ABI will
5165+
/// stop functioning.
5166+
pub WASM_C_ABI,
5167+
Warn,
5168+
"detects code relying on rustc's non-spec-compliant wasm C ABI",
5169+
@future_incompatible = FutureIncompatibleInfo {
5170+
reason: FutureIncompatibilityReason::FutureReleaseErrorReportInDeps,
5171+
reference: "issue #122532 <https://github.com/rust-lang/rust/issues/122532>",
5172+
};
5173+
}

‎compiler/rustc_monomorphize/messages.ftl

+7
Original file line numberDiff line numberDiff line change
@@ -63,4 +63,11 @@ monomorphize_symbol_already_defined = symbol `{$symbol}` is already defined
6363
monomorphize_unknown_cgu_collection_mode =
6464
unknown codegen-item collection mode '{$mode}', falling back to 'lazy' mode
6565
66+
monomorphize_wasm_c_abi_transition =
67+
this function function {$is_call ->
68+
[true] call
69+
*[false] definition
70+
} is affected by the wasm ABI transition: it passes an argument of non-scalar type `{$ty}`
71+
.help = the "C" ABI Rust uses on wasm32-unknown-unknown will change to align with the standard "C" ABI for this target
72+
6673
monomorphize_written_to_path = the full type name has been written to '{$path}'

‎compiler/rustc_monomorphize/src/errors.rs

+9
Original file line numberDiff line numberDiff line change
@@ -103,3 +103,12 @@ pub(crate) struct AbiRequiredTargetFeature<'a> {
103103
/// Whether this is a problem at a call site or at a declaration.
104104
pub is_call: bool,
105105
}
106+
107+
#[derive(LintDiagnostic)]
108+
#[diag(monomorphize_wasm_c_abi_transition)]
109+
#[help]
110+
pub(crate) struct WasmCAbiTransition<'a> {
111+
pub ty: Ty<'a>,
112+
/// Whether this is a problem at a call site or at a declaration.
113+
pub is_call: bool,
114+
}

‎compiler/rustc_monomorphize/src/mono_checks/abi_check.rs

+67-12
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,13 @@
33
use rustc_abi::{BackendRepr, RegKind};
44
use rustc_hir::CRATE_HIR_ID;
55
use rustc_middle::mir::{self, traversal};
6-
use rustc_middle::ty::{self, Instance, InstanceKind, Ty, TyCtxt};
7-
use rustc_session::lint::builtin::ABI_UNSUPPORTED_VECTOR_TYPES;
6+
use rustc_middle::ty::layout::LayoutCx;
7+
use rustc_middle::ty::{self, Instance, InstanceKind, Ty, TyCtxt, TypingEnv};
8+
use rustc_session::lint::builtin::{ABI_UNSUPPORTED_VECTOR_TYPES, WASM_C_ABI};
89
use rustc_span::def_id::DefId;
910
use rustc_span::{DUMMY_SP, Span, Symbol, sym};
10-
use rustc_target::callconv::{Conv, FnAbi, PassMode};
11+
use rustc_target::callconv::{ArgAbi, Conv, FnAbi, PassMode};
12+
use rustc_target::spec::{HasWasmCAbiOpt, WasmCAbi};
1113

1214
use crate::errors;
1315

@@ -26,13 +28,15 @@ fn uses_vector_registers(mode: &PassMode, repr: &BackendRepr) -> bool {
2628
/// for a certain function.
2729
/// `is_call` indicates whether this is a call-site check or a definition-site check;
2830
/// this is only relevant for the wording in the emitted error.
29-
fn do_check_abi<'tcx>(
31+
fn do_check_simd_vector_abi<'tcx>(
3032
tcx: TyCtxt<'tcx>,
3133
abi: &FnAbi<'tcx, Ty<'tcx>>,
3234
def_id: DefId,
3335
is_call: bool,
3436
span: impl Fn() -> Span,
3537
) {
38+
// We check this on all functions, including those using the "Rust" ABI.
39+
// For the "Rust" ABI it would be a bug if the lint ever triggered, but better safe than sorry.
3640
let feature_def = tcx.sess.target.features_for_correct_vector_abi();
3741
let codegen_attrs = tcx.codegen_fn_attrs(def_id);
3842
let have_feature = |feat: Symbol| {
@@ -88,6 +92,59 @@ fn do_check_abi<'tcx>(
8892
}
8993
}
9094

95+
fn wasm_abi_safe<'tcx>(tcx: TyCtxt<'tcx>, arg: &ArgAbi<'tcx, Ty<'tcx>>) -> bool {
96+
if matches!(arg.layout.backend_repr, BackendRepr::Scalar(_)) {
97+
return true;
98+
}
99+
100+
// This matches `unwrap_trivial_aggregate` in the wasm ABI logic.`
101+
if arg.layout.is_aggregate() {
102+
let cx = LayoutCx::new(tcx, TypingEnv::fully_monomorphized());
103+
if let Some(unit) = arg.layout.homogeneous_aggregate(&cx).ok().and_then(|ha| ha.unit()) {
104+
let size = arg.layout.size;
105+
if unit.size == size {
106+
return true;
107+
}
108+
}
109+
}
110+
111+
false
112+
}
113+
114+
/// Warns against usage of `extern "C"` on wasm32-unknown-unknown that is affected by the
115+
/// ABI transition.
116+
fn do_check_wasm_abi<'tcx>(
117+
tcx: TyCtxt<'tcx>,
118+
abi: &FnAbi<'tcx, Ty<'tcx>>,
119+
is_call: bool,
120+
span: impl Fn() -> Span,
121+
) {
122+
// Only proceed for `extern "C" fn`` on wasm32-unknown-unknown (same check as what `adjust_for_foreign_abi` uses to call `compute_wasm_abi_info`).
123+
if !(tcx.sess.target.arch == "wasm32"
124+
&& tcx.sess.target.os == "unknown"
125+
&& tcx.wasm_c_abi_opt() == WasmCAbi::Legacy
126+
&& abi.conv == Conv::C)
127+
{
128+
return;
129+
}
130+
// Warn against all types whose ABI will change. That's all arguments except for things passed as scalars.
131+
// Return values are not affected by this change.
132+
for arg_abi in abi.args.iter() {
133+
if wasm_abi_safe(tcx, arg_abi) {
134+
continue;
135+
}
136+
let span = span();
137+
tcx.emit_node_span_lint(
138+
WASM_C_ABI,
139+
CRATE_HIR_ID,
140+
span,
141+
errors::WasmCAbiTransition { ty: arg_abi.layout.ty, is_call },
142+
);
143+
// Let's only warn once per function.
144+
break;
145+
}
146+
}
147+
91148
/// Checks that the ABI of a given instance of a function does not contain vector-passed arguments
92149
/// or return values for which the corresponding target feature is not enabled.
93150
fn check_instance_abi<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) {
@@ -98,13 +155,10 @@ fn check_instance_abi<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) {
98155
// function.
99156
return;
100157
};
101-
do_check_abi(
102-
tcx,
103-
abi,
104-
instance.def_id(),
105-
/*is_call*/ false,
106-
|| tcx.def_span(instance.def_id()),
107-
)
158+
do_check_simd_vector_abi(tcx, abi, instance.def_id(), /*is_call*/ false, || {
159+
tcx.def_span(instance.def_id())
160+
});
161+
do_check_wasm_abi(tcx, abi, /*is_call*/ false, || tcx.def_span(instance.def_id()));
108162
}
109163

110164
/// Checks that a call expression does not try to pass a vector-passed argument which requires a
@@ -141,7 +195,8 @@ fn check_call_site_abi<'tcx>(
141195
// ABI failed to compute; this will not get through codegen.
142196
return;
143197
};
144-
do_check_abi(tcx, callee_abi, caller.def_id(), /*is_call*/ true, || span);
198+
do_check_simd_vector_abi(tcx, callee_abi, caller.def_id(), /*is_call*/ true, || span);
199+
do_check_wasm_abi(tcx, callee_abi, /*is_call*/ true, || span);
145200
}
146201

147202
fn check_callees_abi<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>, body: &mir::Body<'tcx>) {

‎tests/ui/abi/compatibility.rs

-1
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,6 @@
6262
//@[nvptx64] needs-llvm-components: nvptx
6363
#![feature(no_core, rustc_attrs, lang_items)]
6464
#![feature(unsized_fn_params, transparent_unions)]
65-
#![no_std]
6665
#![no_core]
6766
#![allow(unused, improper_ctypes_definitions, internal_features)]
6867

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
//@ compile-flags: --target wasm32-unknown-unknown
2+
//@ needs-llvm-components: webassembly
3+
//@ add-core-stubs
4+
//@ build-fail
5+
6+
#![feature(no_core)]
7+
#![no_core]
8+
#![crate_type = "lib"]
9+
#![deny(wasm_c_abi)]
10+
11+
extern crate minicore;
12+
use minicore::*;
13+
14+
pub extern "C" fn my_fun_trivial(_x: i32, _y: f32) {}
15+
16+
#[repr(C)]
17+
pub struct MyType(i32, i32);
18+
pub extern "C" fn my_fun(_x: MyType) {} //~ERROR: wasm ABI transition
19+
//~^WARN: previously accepted
20+
21+
// This one is ABI-safe as it only wraps a single field,
22+
// and the return type can be anything.
23+
#[repr(C)]
24+
pub struct MySafeType(i32);
25+
pub extern "C" fn my_fun_safe(_x: MySafeType) -> MyType { loop {} }
26+
27+
// Check call-site warning
28+
extern "C" {
29+
fn other_fun(x: MyType);
30+
}
31+
32+
pub fn call_other_fun(x: MyType) {
33+
unsafe { other_fun(x) } //~ERROR: wasm ABI transition
34+
//~^WARN: previously accepted
35+
}
+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
error: this function function definition is affected by the wasm ABI transition: it passes an argument of non-scalar type `MyType`
2+
--> $DIR/wasm_c_abi_transition.rs:18:1
3+
|
4+
LL | pub extern "C" fn my_fun(_x: MyType) {}
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
6+
|
7+
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
8+
= note: for more information, see issue #122532 <https://github.com/rust-lang/rust/issues/122532>
9+
= help: the "C" ABI Rust uses on wasm32-unknown-unknown will change to align with the standard "C" ABI for this target
10+
note: the lint level is defined here
11+
--> $DIR/wasm_c_abi_transition.rs:9:9
12+
|
13+
LL | #![deny(wasm_c_abi)]
14+
| ^^^^^^^^^^
15+
16+
error: this function function call is affected by the wasm ABI transition: it passes an argument of non-scalar type `MyType`
17+
--> $DIR/wasm_c_abi_transition.rs:33:14
18+
|
19+
LL | unsafe { other_fun(x) }
20+
| ^^^^^^^^^^^^
21+
|
22+
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
23+
= note: for more information, see issue #122532 <https://github.com/rust-lang/rust/issues/122532>
24+
= help: the "C" ABI Rust uses on wasm32-unknown-unknown will change to align with the standard "C" ABI for this target
25+
26+
error: aborting due to 2 previous errors
27+
28+
Future incompatibility report: Future breakage diagnostic:
29+
error: this function function definition is affected by the wasm ABI transition: it passes an argument of non-scalar type `MyType`
30+
--> $DIR/wasm_c_abi_transition.rs:18:1
31+
|
32+
LL | pub extern "C" fn my_fun(_x: MyType) {}
33+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
34+
|
35+
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
36+
= note: for more information, see issue #122532 <https://github.com/rust-lang/rust/issues/122532>
37+
= help: the "C" ABI Rust uses on wasm32-unknown-unknown will change to align with the standard "C" ABI for this target
38+
note: the lint level is defined here
39+
--> $DIR/wasm_c_abi_transition.rs:9:9
40+
|
41+
LL | #![deny(wasm_c_abi)]
42+
| ^^^^^^^^^^
43+
44+
Future breakage diagnostic:
45+
error: this function function call is affected by the wasm ABI transition: it passes an argument of non-scalar type `MyType`
46+
--> $DIR/wasm_c_abi_transition.rs:33:14
47+
|
48+
LL | unsafe { other_fun(x) }
49+
| ^^^^^^^^^^^^
50+
|
51+
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
52+
= note: for more information, see issue #122532 <https://github.com/rust-lang/rust/issues/122532>
53+
= help: the "C" ABI Rust uses on wasm32-unknown-unknown will change to align with the standard "C" ABI for this target
54+
note: the lint level is defined here
55+
--> $DIR/wasm_c_abi_transition.rs:9:9
56+
|
57+
LL | #![deny(wasm_c_abi)]
58+
| ^^^^^^^^^^
59+

0 commit comments

Comments
 (0)
Failed to load comments.