diff --git a/Cargo.lock b/Cargo.lock index 8ac10d7d4fb53..c273e031899de 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4175,6 +4175,7 @@ dependencies = [ "rustc_parse_format", "rustc_session", "rustc_span", + "rustc_symbol_mangling", "rustc_target", "rustc_trait_selection", "smallvec", diff --git a/compiler/rustc_error_codes/src/error_codes/E0755.md b/compiler/rustc_error_codes/src/error_codes/E0755.md index bd93626a8db4d..b194e11b7f85e 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0755.md +++ b/compiler/rustc_error_codes/src/error_codes/E0755.md @@ -20,7 +20,7 @@ side effects or infinite loops: extern "C" { #[unsafe(ffi_pure)] // ok! - pub fn strlen(s: *const i8) -> isize; + pub fn strlen(s: *const std::ffi::c_char) -> usize; } # fn main() {} ``` diff --git a/compiler/rustc_error_codes/src/error_codes/E0756.md b/compiler/rustc_error_codes/src/error_codes/E0756.md index daafc2a5ac092..74233a6ad7a7a 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0756.md +++ b/compiler/rustc_error_codes/src/error_codes/E0756.md @@ -21,7 +21,7 @@ which have no side effects except for their return value: extern "C" { #[unsafe(ffi_const)] // ok! - pub fn strlen(s: *const i8) -> i32; + pub fn strlen(s: *const std::ffi::c_char) -> usize; } # fn main() {} ``` diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index bdf63abc7a859..b28353379dfd6 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -447,6 +447,14 @@ language_item_table! { // Used to fallback `{float}` to `f32` when `f32: From<{float}>` From, sym::From, from_trait, Target::Trait, GenericRequirement::Exact(1); + + // Runtime symbols + MemCpy, sym::memcpy_fn, memcpy_fn, Target::Fn, GenericRequirement::None; + MemMove, sym::memmove_fn, memmove_fn, Target::Fn, GenericRequirement::None; + MemSet, sym::memset_fn, memset_fn, Target::Fn, GenericRequirement::None; + MemCmp, sym::memcmp_fn, memcmp_fn, Target::Fn, GenericRequirement::None; + Bcmp, sym::bcmp_fn, bcmp_fn, Target::Fn, GenericRequirement::None; + StrLen, sym::strlen_fn, strlen_fn, Target::Fn, GenericRequirement::None; } /// The requirement imposed on the generics of a lang item diff --git a/compiler/rustc_hir/src/weak_lang_items.rs b/compiler/rustc_hir/src/weak_lang_items.rs index b4e548effd46d..769bedce34788 100644 --- a/compiler/rustc_hir/src/weak_lang_items.rs +++ b/compiler/rustc_hir/src/weak_lang_items.rs @@ -23,8 +23,29 @@ macro_rules! weak_lang_items { } } +macro_rules! decl_lang_items { + ($($item:ident,)*) => { + pub static DECL_LANG_ITEMS: &[LangItem] = &[$(LangItem::$item,)*]; + + impl LangItem { + pub fn is_decl(self) -> bool { + matches!(self, $(LangItem::$item)|*) + } + } + } +} + weak_lang_items! { PanicImpl, rust_begin_unwind; EhPersonality, rust_eh_personality; EhCatchTypeinfo, rust_eh_catch_typeinfo; } + +decl_lang_items! { + MemCpy, + MemMove, + MemSet, + MemCmp, + Bcmp, + StrLen, +} diff --git a/compiler/rustc_lint/Cargo.toml b/compiler/rustc_lint/Cargo.toml index 758d2762a6af4..176890bab85f2 100644 --- a/compiler/rustc_lint/Cargo.toml +++ b/compiler/rustc_lint/Cargo.toml @@ -22,6 +22,7 @@ rustc_middle = { path = "../rustc_middle" } rustc_parse_format = { path = "../rustc_parse_format" } rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } +rustc_symbol_mangling = { path = "../rustc_symbol_mangling" } rustc_target = { path = "../rustc_target" } rustc_trait_selection = { path = "../rustc_trait_selection" } smallvec = { version = "1.8.1", features = ["union", "may_dangle"] } diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 9fa5501433453..cd3dd8984a880 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -69,6 +69,7 @@ mod precedence; mod ptr_nulls; mod redundant_semicolon; mod reference_casting; +mod runtime_symbols; mod shadowed_into_iter; mod static_mut_refs; mod traits; @@ -112,6 +113,7 @@ use precedence::*; use ptr_nulls::*; use redundant_semicolon::*; use reference_casting::*; +use runtime_symbols::*; use rustc_hir::def_id::LocalModDefId; use rustc_middle::query::Providers; use rustc_middle::ty::TyCtxt; @@ -241,6 +243,7 @@ late_lint_methods!( AsyncFnInTrait: AsyncFnInTrait, NonLocalDefinitions: NonLocalDefinitions::default(), InteriorMutableConsts: InteriorMutableConsts, + RuntimeSymbols: RuntimeSymbols, ImplTraitOvercaptures: ImplTraitOvercaptures, IfLetRescope: IfLetRescope::default(), StaticMutRefs: StaticMutRefs, diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index b92efc408ae81..8a10a69d1a6ae 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -848,6 +848,33 @@ pub(crate) enum UseLetUnderscoreIgnoreSuggestion { }, } +// runtime_symbols.rs +#[derive(Diagnostic)] +pub(crate) enum RedefiningRuntimeSymbolsDiag<'tcx> { + #[diag( + "invalid definition of the runtime `{$symbol_name}` symbol used by the standard library" + )] + #[note( + "expected `{$expected_fn_sig}` + found `{$found_fn_sig}`" + )] + #[help( + "either fix the signature or remove any attributes like `#[unsafe(no_mangle)]`, `#[unsafe(export_name = \"{$symbol_name}\")]`, or `#[link_name = \"{$symbol_name}\"]`" + )] + FnDef { symbol_name: String, expected_fn_sig: Ty<'tcx>, found_fn_sig: Ty<'tcx> }, + #[diag( + "invalid definition of the runtime `{$symbol_name}` symbol used by the standard library" + )] + #[note( + "expected `{$expected_fn_sig}` + found `static {$symbol_name}: {$static_ty}`" + )] + #[help( + "either fix the signature or remove any attributes `#[unsafe(no_mangle)]` or `#[unsafe(export_name = \"{$symbol_name}\")]`" + )] + Static { symbol_name: String, static_ty: Ty<'tcx>, expected_fn_sig: Ty<'tcx> }, +} + // drop_forget_useless.rs #[derive(Diagnostic)] #[diag("calls to `std::mem::drop` with a reference instead of an owned value does nothing")] diff --git a/compiler/rustc_lint/src/runtime_symbols.rs b/compiler/rustc_lint/src/runtime_symbols.rs new file mode 100644 index 0000000000000..81e1fe40d0162 --- /dev/null +++ b/compiler/rustc_lint/src/runtime_symbols.rs @@ -0,0 +1,205 @@ +use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_hir::{self as hir, FnSig, ForeignItemKind, LanguageItems}; +use rustc_infer::infer::DefineOpaqueTypes; +use rustc_middle::ty::{self, Instance, Ty}; +use rustc_session::{declare_lint, declare_lint_pass}; +use rustc_span::Span; +use rustc_trait_selection::infer::TyCtxtInferExt; + +use crate::lints::RedefiningRuntimeSymbolsDiag; +use crate::{LateContext, LateLintPass, LintContext}; + +declare_lint! { + /// The `invalid_runtime_symbol_definitions` lint checks the signature of items whose + /// symbol name is a runtime symbols expected by `core`. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// #[unsafe(no_mangle)] + /// pub fn strlen() {} // invalid definition of the `strlen` function + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Up-most care is required when defining runtime symbols assumed and + /// used by the standard library. They must follow the C specification, not use any + /// standard-library facility or undefined behavior may occur. + /// + /// The symbols currently checked are `memcpy`, `memmove`, `memset`, `memcmp`, + /// `bcmp` and `strlen`. + /// + /// [^1]: https://doc.rust-lang.org/core/index.html#how-to-use-the-core-library + pub INVALID_RUNTIME_SYMBOL_DEFINITIONS, + Deny, + "invalid definition of a symbol used by the standard library" +} + +declare_lint_pass!(RuntimeSymbols => [INVALID_RUNTIME_SYMBOL_DEFINITIONS]); + +static EXPECTED_SYMBOLS: &[ExpectedSymbol] = &[ + ExpectedSymbol { symbol: "memcpy", lang: LanguageItems::memcpy_fn }, + ExpectedSymbol { symbol: "memmove", lang: LanguageItems::memmove_fn }, + ExpectedSymbol { symbol: "memset", lang: LanguageItems::memset_fn }, + ExpectedSymbol { symbol: "memcmp", lang: LanguageItems::memcmp_fn }, + ExpectedSymbol { symbol: "bcmp", lang: LanguageItems::bcmp_fn }, + ExpectedSymbol { symbol: "strlen", lang: LanguageItems::strlen_fn }, +]; + +#[derive(Copy, Clone, Debug)] +struct ExpectedSymbol { + symbol: &'static str, + lang: fn(&LanguageItems) -> Option, +} + +impl<'tcx> LateLintPass<'tcx> for RuntimeSymbols { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) { + // Bail-out if the item is not a function/method or static. + match item.kind { + hir::ItemKind::Fn { sig, ident: _, generics, body: _, has_body: _ } => { + // Generic functions cannot have the same runtime symbol as we do not allow + // any symbol attributes. + if !generics.params.is_empty() { + return; + } + + // Try to the overridden symbol name of this function (our mangling + // cannot ever conflict with runtime symbols, so no need to check for those). + let Some(symbol_name) = rustc_symbol_mangling::symbol_name_from_attrs( + cx.tcx, + rustc_middle::ty::InstanceKind::Item(item.owner_id.to_def_id()), + ) else { + return; + }; + + check_fn(cx, &symbol_name, sig, item.owner_id.def_id); + } + hir::ItemKind::Static(..) => { + // Compute the symbol name of this static (without mangling, as our mangling + // cannot ever conflict with runtime symbols). + let Some(symbol_name) = rustc_symbol_mangling::symbol_name_from_attrs( + cx.tcx, + rustc_middle::ty::InstanceKind::Item(item.owner_id.to_def_id()), + ) else { + return; + }; + + let def_id = item.owner_id.def_id; + + check_static(cx, &symbol_name, def_id, item.span); + } + hir::ItemKind::ForeignMod { abi: _, items } => { + for item in items { + let item = cx.tcx.hir_foreign_item(*item); + + let did = item.owner_id.def_id; + let instance = Instance::new_raw( + did.to_def_id(), + ty::List::identity_for_item(cx.tcx, did), + ); + let symbol_name = cx.tcx.symbol_name(instance); + + match item.kind { + ForeignItemKind::Fn(fn_sig, _idents, _generics) => { + check_fn(cx, &symbol_name.name, fn_sig, did); + } + ForeignItemKind::Static(..) => { + check_static(cx, &symbol_name.name, did, item.span); + } + ForeignItemKind::Type => return, + } + } + } + _ => return, + } + } +} + +fn check_fn(cx: &LateContext<'_>, symbol_name: &str, sig: FnSig<'_>, did: LocalDefId) { + let Some(expected_symbol) = EXPECTED_SYMBOLS.iter().find(|es| es.symbol == symbol_name) else { + // The symbol name does not correspond to a runtime symbols, bail out + return; + }; + + let Some(expected_def_id) = (expected_symbol.lang)(&cx.tcx.lang_items()) else { + // Can't find the corresponding language item, bail out + return; + }; + + // Get the two function signatures + let lang_sig = cx.tcx.normalize_erasing_regions( + cx.typing_env(), + cx.tcx.fn_sig(expected_def_id).instantiate_identity(), + ); + let user_sig = cx + .tcx + .normalize_erasing_regions(cx.typing_env(), cx.tcx.fn_sig(did).instantiate_identity()); + + // Compare the two signatures with an inference context + let infcx = cx.tcx.infer_ctxt().build(cx.typing_mode()); + let cause = rustc_middle::traits::ObligationCause::misc(sig.span, did); + let result = infcx.at(&cause, cx.param_env).eq(DefineOpaqueTypes::No, lang_sig, user_sig); + + // If they don't match, emit our own mismatch signatures + if let Err(_terr) = result { + // Create fn pointers for diagnostics purpose + let expected = Ty::new_fn_ptr(cx.tcx, lang_sig); + let actual = Ty::new_fn_ptr(cx.tcx, user_sig); + + cx.emit_span_lint( + INVALID_RUNTIME_SYMBOL_DEFINITIONS, + sig.span, + RedefiningRuntimeSymbolsDiag::FnDef { + symbol_name: symbol_name.to_string(), + found_fn_sig: actual, + expected_fn_sig: expected, + }, + ); + } +} + +fn check_static<'tcx>(cx: &LateContext<'tcx>, symbol_name: &str, did: LocalDefId, sp: Span) { + let Some(expected_symbol) = EXPECTED_SYMBOLS.iter().find(|es| es.symbol == symbol_name) else { + // The symbol name does not correspond to a runtime symbols, bail out + return; + }; + + let Some(expected_def_id) = (expected_symbol.lang)(&cx.tcx.lang_items()) else { + // Can't find the corresponding language item, bail out + return; + }; + + // Get the static type + let static_ty = cx.tcx.type_of(did).instantiate_identity().skip_norm_wip(); + + // Peel Option<...> and get the inner type (see std weak! macro with #[linkage = "extern_weak"]) + let inner_static_ty: Ty<'_> = match static_ty.kind() { + ty::Adt(def, args) if Some(def.did()) == cx.tcx.lang_items().option_type() => { + args.type_at(0) + } + _ => static_ty, + }; + + // Get the expected symbol function signature + let lang_sig = cx.tcx.normalize_erasing_regions( + cx.typing_env(), + cx.tcx.fn_sig(expected_def_id).instantiate_identity(), + ); + + let expected = Ty::new_fn_ptr(cx.tcx, lang_sig); + + // Compare the expected function signature with the static type, report an error if they don't match + if expected != inner_static_ty { + cx.emit_span_lint( + INVALID_RUNTIME_SYMBOL_DEFINITIONS, + sp, + RedefiningRuntimeSymbolsDiag::Static { + static_ty, + symbol_name: symbol_name.to_string(), + expected_fn_sig: expected, + }, + ); + } +} diff --git a/compiler/rustc_passes/src/lang_items.rs b/compiler/rustc_passes/src/lang_items.rs index b6fb9937dfa45..79c8b71b91645 100644 --- a/compiler/rustc_passes/src/lang_items.rs +++ b/compiler/rustc_passes/src/lang_items.rs @@ -29,6 +29,11 @@ pub(crate) enum Duplicate { CrateDepends, } +enum CollectWeak { + Allowed, + Ignore, +} + struct LanguageItemCollector<'ast, 'tcx> { items: LanguageItems, tcx: TyCtxt<'tcx>, @@ -60,19 +65,23 @@ impl<'ast, 'tcx> LanguageItemCollector<'ast, 'tcx> { attrs: &'ast [ast::Attribute], item_span: Span, generics: Option<&'ast ast::Generics>, + collect_weak: CollectWeak, ) { if let Some((name, attr_span)) = extract_ast(attrs) { match LangItem::from_name(name) { // Known lang item with attribute on correct target. Some(lang_item) if actual_target == lang_item.target() => { - self.collect_item_extended( - lang_item, - def_id, - item_span, - attr_span, - generics, - actual_target, - ); + // Weak lang items are handled separately + if !lang_item.is_weak() || matches!(collect_weak, CollectWeak::Allowed) { + self.collect_item_extended( + lang_item, + def_id, + item_span, + attr_span, + generics, + actual_target, + ); + } } // Known lang item with attribute on incorrect target. Some(lang_item) => { @@ -299,6 +308,7 @@ impl<'ast, 'tcx> visit::Visitor<'ast> for LanguageItemCollector<'ast, 'tcx> { &i.attrs, i.span, i.opt_generics(), + CollectWeak::Allowed, ); let parent_item = self.parent_item.replace(i); @@ -306,6 +316,17 @@ impl<'ast, 'tcx> visit::Visitor<'ast> for LanguageItemCollector<'ast, 'tcx> { self.parent_item = parent_item; } + fn visit_foreign_item(&mut self, i: &'ast ast::ForeignItem) { + self.check_for_lang( + Target::Fn, + self.resolver.node_id_to_def_id[&i.id], + &i.attrs, + i.span, + None, + CollectWeak::Ignore, + ); + } + fn visit_variant(&mut self, variant: &'ast ast::Variant) { self.check_for_lang( Target::Variant, @@ -313,6 +334,7 @@ impl<'ast, 'tcx> visit::Visitor<'ast> for LanguageItemCollector<'ast, 'tcx> { &variant.attrs, variant.span, None, + CollectWeak::Allowed, ); } @@ -352,6 +374,7 @@ impl<'ast, 'tcx> visit::Visitor<'ast> for LanguageItemCollector<'ast, 'tcx> { &i.attrs, i.span, generics, + CollectWeak::Allowed, ); visit::walk_assoc_item(self, i, ctxt); diff --git a/compiler/rustc_passes/src/weak_lang_items.rs b/compiler/rustc_passes/src/weak_lang_items.rs index 4200003ea1d1a..94d841bdea364 100644 --- a/compiler/rustc_passes/src/weak_lang_items.rs +++ b/compiler/rustc_passes/src/weak_lang_items.rs @@ -48,11 +48,15 @@ struct WeakLangItemVisitor<'a, 'tcx> { impl<'ast> visit::Visitor<'ast> for WeakLangItemVisitor<'_, '_> { fn visit_foreign_item(&mut self, i: &'ast ast::ForeignItem) { if let Some((lang_item, _)) = extract_ast(&i.attrs) { - if let Some(item) = LangItem::from_name(lang_item) - && item.is_weak() - { - if self.items.get(item).is_none() { - self.items.missing.push(item); + if let Some(item) = LangItem::from_name(lang_item) { + if item.is_weak() { + if self.items.get(item).is_none() { + self.items.missing.push(item); + } + } else if item.is_decl() { + // delc lang items are handled directly in lang_items.rs + } else { + self.tcx.dcx().emit_err(UnknownExternLangItem { span: i.span, lang_item }); } } else { self.tcx.dcx().emit_err(UnknownExternLangItem { span: i.span, lang_item }); diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 981bfed363dcc..623da17902a63 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -508,6 +508,7 @@ symbols! { backchain, backend_repr, bang, + bcmp_fn, begin_panic, bench, bevy_ecs, @@ -1248,7 +1249,11 @@ symbols! { mem_variant_count, mem_zeroed, member_constraints, + memcmp_fn, + memcpy_fn, + memmove_fn, memory, + memset_fn, memtag, message, meta, @@ -1983,6 +1988,7 @@ symbols! { strict_provenance_lints, string_deref_patterns, stringify, + strlen_fn, struct_field_attributes, struct_inherit, struct_variant, diff --git a/compiler/rustc_symbol_mangling/src/lib.rs b/compiler/rustc_symbol_mangling/src/lib.rs index c052037f05b39..693aa95b2524a 100644 --- a/compiler/rustc_symbol_mangling/src/lib.rs +++ b/compiler/rustc_symbol_mangling/src/lib.rs @@ -92,7 +92,7 @@ use rustc_hir::def_id::{CrateNum, LOCAL_CRATE}; use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs}; use rustc_middle::mono::{InstantiationMode, MonoItem}; use rustc_middle::query::Providers; -use rustc_middle::ty::{self, Instance, TyCtxt}; +use rustc_middle::ty::{self, Instance, InstanceKind, TyCtxt}; use rustc_session::config::SymbolManglingVersion; use tracing::debug; @@ -149,29 +149,22 @@ pub fn typeid_for_trait_ref<'tcx>( v0::mangle_typeid_for_trait_ref(tcx, trait_ref) } -/// Computes the symbol name for the given instance. This function will call -/// `compute_instantiating_crate` if it needs to factor the instantiating crate -/// into the symbol name. -fn compute_symbol_name<'tcx>( +pub fn symbol_name_from_attrs<'tcx>( tcx: TyCtxt<'tcx>, - instance: Instance<'tcx>, - compute_instantiating_crate: impl FnOnce() -> CrateNum, -) -> String { - let def_id = instance.def_id(); - let args = instance.args; - - debug!("symbol_name(def_id={:?}, args={:?})", def_id, args); + instance_kind: InstanceKind<'tcx>, +) -> Option { + let def_id = instance_kind.def_id(); if let Some(def_id) = def_id.as_local() { if tcx.proc_macro_decls_static(()) == Some(def_id) { let stable_crate_id = tcx.stable_crate_id(LOCAL_CRATE); - return tcx.sess.generate_proc_macro_decls_symbol(stable_crate_id); + return Some(tcx.sess.generate_proc_macro_decls_symbol(stable_crate_id)); } } // FIXME(eddyb) Precompute a custom symbol name based on attributes. let attrs = if tcx.def_kind(def_id).has_codegen_attrs() { - &tcx.codegen_instance_attrs(instance.def) + &tcx.codegen_instance_attrs(instance_kind) } else { CodegenFnAttrs::EMPTY }; @@ -197,7 +190,7 @@ fn compute_symbol_name<'tcx>( // legacy symbol mangling scheme. let name = if let Some(name) = attrs.symbol_name { name } else { tcx.item_name(def_id) }; - return v0::mangle_internal_symbol(tcx, name.as_str()); + return Some(v0::mangle_internal_symbol(tcx, name.as_str())); } let wasm_import_module_exception_force_mangling = { @@ -225,15 +218,35 @@ fn compute_symbol_name<'tcx>( if !wasm_import_module_exception_force_mangling { if let Some(name) = attrs.symbol_name { // Use provided name - return name.to_string(); + return Some(name.to_string()); } if attrs.flags.contains(CodegenFnAttrFlags::NO_MANGLE) { // Don't mangle - return tcx.item_name(def_id).to_string(); + return Some(tcx.item_name(def_id).to_string()); } } + None +} + +/// Computes the symbol name for the given instance. This function will call +/// `compute_instantiating_crate` if it needs to factor the instantiating crate +/// into the symbol name. +fn compute_symbol_name<'tcx>( + tcx: TyCtxt<'tcx>, + instance: Instance<'tcx>, + compute_instantiating_crate: impl FnOnce() -> CrateNum, +) -> String { + let def_id = instance.def_id(); + let args = instance.args; + + debug!("symbol_name(def_id={:?}, args={:?})", def_id, args); + + if let Some(symbol) = symbol_name_from_attrs(tcx, instance.def) { + return symbol; + } + // If we're dealing with an instance of a function that's inlined from // another crate but we're marking it as globally shared to our // compilation (aka we're not making an internal copy in each of our diff --git a/library/core/src/ffi/mod.rs b/library/core/src/ffi/mod.rs index f1b928da7ef3c..de226b2d60d1f 100644 --- a/library/core/src/ffi/mod.rs +++ b/library/core/src/ffi/mod.rs @@ -91,3 +91,30 @@ impl fmt::Debug for c_void { )] #[link(name = "/defaultlib:libcmt", modifiers = "+verbatim", cfg(target_feature = "crt-static"))] unsafe extern "C" {} + +// Used by rustc for checking the definitions of other function with the same symbol names +// +// See the `invalid_runtime_symbols_definitions` lint. +mod runtime_symbols { + use crate::ffi::{c_char, c_int, c_void}; + + unsafe extern "C" { + #[lang = "memcpy_fn"] + fn memcpy(dest: *mut c_void, src: *const c_void, n: usize) -> *mut c_void; + + #[lang = "memmove_fn"] + fn memmove(dest: *mut c_void, src: *const c_void, n: usize) -> *mut c_void; + + #[lang = "memset_fn"] + fn memset(s: *mut c_void, c: c_int, n: usize) -> *mut c_void; + + #[lang = "memcmp_fn"] + fn memcmp(s1: *const c_void, s2: *const c_void, n: usize) -> c_int; + + #[lang = "bcmp_fn"] + fn bcmp(s1: *const c_void, s2: *const c_void, n: usize) -> c_int; + + #[lang = "strlen_fn"] + fn strlen(s: *const c_char) -> usize; + } +} diff --git a/tests/codegen-llvm/no_builtins-at-crate.rs b/tests/codegen-llvm/no_builtins-at-crate.rs index ba1d31f60c390..de80a925aebc5 100644 --- a/tests/codegen-llvm/no_builtins-at-crate.rs +++ b/tests/codegen-llvm/no_builtins-at-crate.rs @@ -3,11 +3,13 @@ #![no_builtins] #![crate_type = "lib"] +use std::ffi::c_void; + // CHECK: define // CHECK-SAME: @__aeabi_memcpy // CHECK-SAME: #0 #[no_mangle] -pub unsafe extern "C" fn __aeabi_memcpy(dest: *mut u8, src: *const u8, size: usize) { +pub unsafe extern "C" fn __aeabi_memcpy(dest: *mut c_void, src: *const c_void, size: usize) { // CHECK: call // CHECK-SAME: @memcpy( memcpy(dest, src, size); @@ -17,7 +19,7 @@ pub unsafe extern "C" fn __aeabi_memcpy(dest: *mut u8, src: *const u8, size: usi // CHECK-SAME: @memcpy // CHECK-SAME: #0 extern "C" { - pub fn memcpy(dest: *mut u8, src: *const u8, n: usize) -> *mut u8; + pub fn memcpy(dest: *mut c_void, src: *const c_void, n: usize) -> *mut c_void; } // CHECK: attributes #0 diff --git a/tests/ui/error-codes/E0264.rs b/tests/ui/error-codes/E0264.rs index 855644796ed45..5974eb05e2c4b 100644 --- a/tests/ui/error-codes/E0264.rs +++ b/tests/ui/error-codes/E0264.rs @@ -1,7 +1,7 @@ #![feature(lang_items)] extern "C" { - #[lang = "copy"] + #[lang = "copy"] //~ ERROR E0718 fn copy(); //~ ERROR E0264 } diff --git a/tests/ui/error-codes/E0264.stderr b/tests/ui/error-codes/E0264.stderr index 6442f42e689d6..aa578194d1f28 100644 --- a/tests/ui/error-codes/E0264.stderr +++ b/tests/ui/error-codes/E0264.stderr @@ -1,9 +1,16 @@ +error[E0718]: `copy` lang item must be applied to a trait + --> $DIR/E0264.rs:4:5 + | +LL | #[lang = "copy"] + | ^^^^^^^^^^^^^^^^ attribute should be applied to a trait, not a function + error[E0264]: unknown external lang item: `copy` --> $DIR/E0264.rs:5:5 | LL | fn copy(); | ^^^^^^^^^^ -error: aborting due to 1 previous error +error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0264`. +Some errors have detailed explanations: E0264, E0718. +For more information about an error, try `rustc --explain E0264`. diff --git a/tests/ui/foreign/foreign-int-types.rs b/tests/ui/foreign/foreign-int-types.rs index d20a4c96ea0e2..bac9d8c603918 100644 --- a/tests/ui/foreign/foreign-int-types.rs +++ b/tests/ui/foreign/foreign-int-types.rs @@ -4,7 +4,7 @@ mod xx { extern "C" { - pub fn strlen(str: *const u8) -> usize; + pub fn strlen2(str: *const u8) -> usize; pub fn foo(x: isize, y: usize); } } diff --git a/tests/ui/lint/runtime-symbols.rs b/tests/ui/lint/runtime-symbols.rs new file mode 100644 index 0000000000000..813ebff4ff4de --- /dev/null +++ b/tests/ui/lint/runtime-symbols.rs @@ -0,0 +1,54 @@ +// This test checks the runtime symbols lint. + +//@ edition: 2021 +//@ normalize-stderr: "\*const [iu]8" -> "*const U8" + +#![allow(clashing_extern_declarations)] // we are volontary testing differents defs + +use core::ffi::{c_char, c_int, c_void}; + +fn invalid() { + #[no_mangle] + pub extern "C" fn memcpy(dest: *mut c_void, src: *const c_void, n: i64) -> *mut c_void { + std::ptr::null_mut() + } + //~^^^ ERROR invalid definition of the runtime `memcpy` symbol + + #[no_mangle] + pub fn memmove() {} + //~^ ERROR invalid definition of the runtime `memmove` symbol + + extern "C" { + pub fn memset(); + //~^ ERROR invalid definition of the runtime `memset` symbol + + pub fn memcmp(); + //~^ ERROR invalid definition of the runtime `memcmp` symbol + } + + #[export_name = "bcmp"] + pub fn bcmp_() {} + //~^ ERROR invalid definition of the runtime `bcmp` symbol + + #[no_mangle] + pub static strlen: () = (); + //~^ ERROR invalid definition of the runtime `strlen` symbol +} + +fn valid() { + extern "C" { + fn memcpy(dest: *mut c_void, src: *const c_void, n: usize) -> *mut c_void; + + fn memmove(dest: *mut c_void, src: *const c_void, n: usize) -> *mut c_void; + + fn memset(s: *mut c_void, c: c_int, n: usize) -> *mut c_void; + + fn memcmp(s1: *const c_void, s2: *const c_void, n: usize) -> c_int; + + fn bcmp(s1: *const c_void, s2: *const c_void, n: usize) -> c_int; + + static strlen: Option usize>; + } +} + +fn main() {} diff --git a/tests/ui/lint/runtime-symbols.stderr b/tests/ui/lint/runtime-symbols.stderr new file mode 100644 index 0000000000000..bac3c352bdca6 --- /dev/null +++ b/tests/ui/lint/runtime-symbols.stderr @@ -0,0 +1,63 @@ +error: invalid definition of the runtime `memcpy` symbol used by the standard library + --> $DIR/runtime-symbols.rs:12:5 + | +LL | pub extern "C" fn memcpy(dest: *mut c_void, src: *const c_void, n: i64) -> *mut c_void { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: expected `unsafe extern "C" fn(*mut c_void, *const c_void, usize) -> *mut c_void` + found `extern "C" fn(*mut c_void, *const c_void, i64) -> *mut c_void` + = help: either fix the signature or remove any attributes like `#[unsafe(no_mangle)]`, `#[unsafe(export_name = "memcpy")]`, or `#[link_name = "memcpy"]` + = note: `#[deny(invalid_runtime_symbol_definitions)]` on by default + +error: invalid definition of the runtime `memmove` symbol used by the standard library + --> $DIR/runtime-symbols.rs:18:5 + | +LL | pub fn memmove() {} + | ^^^^^^^^^^^^^^^^ + | + = note: expected `unsafe extern "C" fn(*mut c_void, *const c_void, usize) -> *mut c_void` + found `fn()` + = help: either fix the signature or remove any attributes like `#[unsafe(no_mangle)]`, `#[unsafe(export_name = "memmove")]`, or `#[link_name = "memmove"]` + +error: invalid definition of the runtime `memset` symbol used by the standard library + --> $DIR/runtime-symbols.rs:22:9 + | +LL | pub fn memset(); + | ^^^^^^^^^^^^^^^^ + | + = note: expected `unsafe extern "C" fn(*mut c_void, i32, usize) -> *mut c_void` + found `unsafe extern "C" fn()` + = help: either fix the signature or remove any attributes like `#[unsafe(no_mangle)]`, `#[unsafe(export_name = "memset")]`, or `#[link_name = "memset"]` + +error: invalid definition of the runtime `memcmp` symbol used by the standard library + --> $DIR/runtime-symbols.rs:25:9 + | +LL | pub fn memcmp(); + | ^^^^^^^^^^^^^^^^ + | + = note: expected `unsafe extern "C" fn(*const c_void, *const c_void, usize) -> i32` + found `unsafe extern "C" fn()` + = help: either fix the signature or remove any attributes like `#[unsafe(no_mangle)]`, `#[unsafe(export_name = "memcmp")]`, or `#[link_name = "memcmp"]` + +error: invalid definition of the runtime `bcmp` symbol used by the standard library + --> $DIR/runtime-symbols.rs:30:5 + | +LL | pub fn bcmp_() {} + | ^^^^^^^^^^^^^^ + | + = note: expected `unsafe extern "C" fn(*const c_void, *const c_void, usize) -> i32` + found `fn()` + = help: either fix the signature or remove any attributes like `#[unsafe(no_mangle)]`, `#[unsafe(export_name = "bcmp")]`, or `#[link_name = "bcmp"]` + +error: invalid definition of the runtime `strlen` symbol used by the standard library + --> $DIR/runtime-symbols.rs:34:5 + | +LL | pub static strlen: () = (); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: expected `unsafe extern "C" fn(*const U8) -> usize` + found `static strlen: ()` + = help: either fix the signature or remove any attributes `#[unsafe(no_mangle)]` or `#[unsafe(export_name = "strlen")]` + +error: aborting due to 6 previous errors +