diff --git a/compiler/rustc_codegen_llvm/src/back/archive.rs b/compiler/rustc_codegen_llvm/src/back/archive.rs index 64416bced31d0..6ac7093b7dee8 100644 --- a/compiler/rustc_codegen_llvm/src/back/archive.rs +++ b/compiler/rustc_codegen_llvm/src/back/archive.rs @@ -12,7 +12,7 @@ use crate::llvm::{self, ArchiveKind, LLVMMachineType, LLVMRustCOFFShortExport}; use rustc_codegen_ssa::back::archive::{find_library, ArchiveBuilder}; use rustc_codegen_ssa::{looks_like_rust_object_file, METADATA_FILENAME}; use rustc_data_structures::temp_dir::MaybeTempDir; -use rustc_middle::middle::cstore::DllImport; +use rustc_middle::middle::cstore::{DllCallingConvention, DllImport}; use rustc_session::Session; use rustc_span::symbol::Symbol; @@ -208,10 +208,12 @@ impl<'a> ArchiveBuilder<'a> for LlvmArchiveBuilder<'a> { // have any \0 characters let import_name_vector: Vec = dll_imports .iter() - .map(if self.config.sess.target.arch == "x86" { - |import: &DllImport| CString::new(format!("_{}", import.name.to_string())).unwrap() - } else { - |import: &DllImport| CString::new(import.name.to_string()).unwrap() + .map(|import: &DllImport| { + if self.config.sess.target.arch == "x86" { + LlvmArchiveBuilder::i686_decorated_name(import) + } else { + CString::new(import.name.to_string()).unwrap() + } }) .collect(); @@ -391,6 +393,21 @@ impl<'a> LlvmArchiveBuilder<'a> { ret } } + + fn i686_decorated_name(import: &DllImport) -> CString { + let name = import.name; + // We verified during construction that `name` does not contain any NULL characters, so the + // conversion to CString is guaranteed to succeed. + CString::new(match import.calling_convention { + DllCallingConvention::C => format!("_{}", name), + DllCallingConvention::Stdcall(arg_list_size) => format!("_{}@{}", name, arg_list_size), + DllCallingConvention::Fastcall(arg_list_size) => format!("@{}@{}", name, arg_list_size), + DllCallingConvention::Vectorcall(arg_list_size) => { + format!("{}@@{}", name, arg_list_size) + } + }) + .unwrap() + } } fn string_to_io_error(s: String) -> io::Error { diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 59447e9de1350..f9efa448c93fa 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -3,7 +3,7 @@ use rustc_data_structures::temp_dir::MaybeTempDir; use rustc_errors::Handler; use rustc_fs_util::fix_windows_verbatim_for_gcc; use rustc_hir::def_id::CrateNum; -use rustc_middle::middle::cstore::DllImport; +use rustc_middle::middle::cstore::{DllCallingConvention, DllImport}; use rustc_middle::middle::dependency_format::Linkage; use rustc_session::config::{self, CFGuard, CrateType, DebugInfo, LdImpl, Strip}; use rustc_session::config::{OutputFilenames, OutputType, PrintRequest}; @@ -34,8 +34,8 @@ use object::write::Object; use object::{Architecture, BinaryFormat, Endianness, FileFlags, SectionFlags, SectionKind}; use tempfile::Builder as TempFileBuilder; -use std::cmp::Ordering; use std::ffi::OsString; +use std::iter::FromIterator; use std::path::{Path, PathBuf}; use std::process::{ExitStatus, Output, Stdio}; use std::{ascii, char, env, fmt, fs, io, mem, str}; @@ -259,7 +259,7 @@ fn link_rlib<'a, B: ArchiveBuilder<'a>>( } for (raw_dylib_name, raw_dylib_imports) in - collate_raw_dylibs(&codegen_results.crate_info.used_libraries) + collate_raw_dylibs(sess, &codegen_results.crate_info.used_libraries) { ab.inject_dll_import_lib(&raw_dylib_name, &raw_dylib_imports, tmpdir); } @@ -451,8 +451,11 @@ fn link_rlib<'a, B: ArchiveBuilder<'a>>( /// then the CodegenResults value contains one NativeLib instance for each block. However, the /// linker appears to expect only a single import library for each library used, so we need to /// collate the symbols together by library name before generating the import libraries. -fn collate_raw_dylibs(used_libraries: &[NativeLib]) -> Vec<(String, Vec)> { - let mut dylib_table: FxHashMap> = FxHashMap::default(); +fn collate_raw_dylibs( + sess: &Session, + used_libraries: &[NativeLib], +) -> Vec<(String, Vec)> { + let mut dylib_table: FxHashMap> = FxHashMap::default(); for lib in used_libraries { if lib.kind == NativeLibKind::RawDylib { @@ -464,35 +467,51 @@ fn collate_raw_dylibs(used_libraries: &[NativeLib]) -> Vec<(String, Vec)> = dylib_table .into_iter() - .map(|(lib_name, imported_names)| { - let mut names = imported_names - .iter() - .map(|name| DllImport { name: *name, ordinal: None }) - .collect::>(); - names.sort_unstable_by(|a: &DllImport, b: &DllImport| { - match a.name.as_str().cmp(&b.name.as_str()) { - Ordering::Equal => a.ordinal.cmp(&b.ordinal), - x => x, - } - }); - (lib_name, names) + .map(|(lib_name, import_table)| { + let mut imports = Vec::from_iter(import_table.into_iter()); + imports.sort_unstable_by_key(|x: &DllImport| x.name.as_str()); + (lib_name, imports) }) .collect::>(); result.sort_unstable_by(|a: &(String, Vec), b: &(String, Vec)| { a.0.cmp(&b.0) }); + let result = result; + + // Check for multiple imports with the same name but different calling conventions or + // (when relevant) argument list sizes. Rustc only signals an error for this if the + // declarations are at the same scope level; if one shadows the other, we only get a lint + // warning. + for (library, imports) in &result { + let mut import_table: FxHashMap = FxHashMap::default(); + for import in imports { + if let Some(old_convention) = + import_table.insert(import.name, import.calling_convention) + { + if import.calling_convention != old_convention { + sess.span_fatal( + import.span, + &format!( + "multiple definitions of external function `{}` from library `{}` have different calling conventions", + import.name, + library, + )); + } + } + } + } + result } diff --git a/compiler/rustc_metadata/src/native_libs.rs b/compiler/rustc_metadata/src/native_libs.rs index cd4c394ae14ec..16b4d26b37b4b 100644 --- a/compiler/rustc_metadata/src/native_libs.rs +++ b/compiler/rustc_metadata/src/native_libs.rs @@ -3,8 +3,8 @@ use rustc_data_structures::fx::FxHashSet; use rustc_errors::struct_span_err; use rustc_hir as hir; use rustc_hir::itemlikevisit::ItemLikeVisitor; -use rustc_middle::middle::cstore::{DllImport, NativeLib}; -use rustc_middle::ty::TyCtxt; +use rustc_middle::middle::cstore::{DllCallingConvention, DllImport, NativeLib}; +use rustc_middle::ty::{List, ParamEnv, ParamEnvAnd, Ty, TyCtxt}; use rustc_session::parse::feature_err; use rustc_session::utils::NativeLibKind; use rustc_session::Session; @@ -199,22 +199,10 @@ impl ItemLikeVisitor<'tcx> for Collector<'tcx> { } if lib.kind == NativeLibKind::RawDylib { - match abi { - Abi::C { .. } => (), - Abi::Cdecl => (), - _ => { - if sess.target.arch == "x86" { - sess.span_fatal( - it.span, - r#"`#[link(kind = "raw-dylib")]` only supports C and Cdecl ABIs"#, - ); - } - } - }; lib.dll_imports.extend( foreign_mod_items .iter() - .map(|child_item| DllImport { name: child_item.ident.name, ordinal: None }), + .map(|child_item| self.build_dll_import(abi, child_item)), ); } @@ -396,4 +384,58 @@ impl Collector<'tcx> { } } } + + fn i686_arg_list_size(&self, item: &hir::ForeignItemRef<'_>) -> usize { + let argument_types: &List> = self.tcx.erase_late_bound_regions( + self.tcx + .type_of(item.id.def_id) + .fn_sig(self.tcx) + .inputs() + .map_bound(|slice| self.tcx.mk_type_list(slice.iter())), + ); + + argument_types + .iter() + .map(|ty| { + let layout = self + .tcx + .layout_of(ParamEnvAnd { param_env: ParamEnv::empty(), value: ty }) + .expect("layout") + .layout; + // In both stdcall and fastcall, we always round up the argument size to the + // nearest multiple of 4 bytes. + (layout.size.bytes_usize() + 3) & !3 + }) + .sum() + } + + fn build_dll_import(&self, abi: Abi, item: &hir::ForeignItemRef<'_>) -> DllImport { + let calling_convention = if self.tcx.sess.target.arch == "x86" { + match abi { + Abi::C { .. } | Abi::Cdecl => DllCallingConvention::C, + Abi::Stdcall { .. } | Abi::System { .. } => { + DllCallingConvention::Stdcall(self.i686_arg_list_size(item)) + } + Abi::Fastcall => DllCallingConvention::Fastcall(self.i686_arg_list_size(item)), + // Vectorcall is intentionally not supported at this time. + _ => { + self.tcx.sess.span_fatal( + item.span, + r#"ABI not supported by `#[link(kind = "raw-dylib")]` on i686"#, + ); + } + } + } else { + match abi { + Abi::C { .. } | Abi::Win64 | Abi::System { .. } => DllCallingConvention::C, + _ => { + self.tcx.sess.span_fatal( + item.span, + r#"ABI not supported by `#[link(kind = "raw-dylib")]` on this architecture"#, + ); + } + } + }; + DllImport { name: item.ident.name, ordinal: None, calling_convention, span: item.span } + } } diff --git a/compiler/rustc_middle/src/middle/cstore.rs b/compiler/rustc_middle/src/middle/cstore.rs index 3c4c4a84d244f..d69904f7b11a2 100644 --- a/compiler/rustc_middle/src/middle/cstore.rs +++ b/compiler/rustc_middle/src/middle/cstore.rs @@ -77,10 +77,29 @@ pub struct NativeLib { pub dll_imports: Vec, } -#[derive(Clone, Debug, Encodable, Decodable, HashStable)] +#[derive(Clone, Debug, PartialEq, Eq, Encodable, Decodable, Hash, HashStable)] pub struct DllImport { pub name: Symbol, pub ordinal: Option, + /// Calling convention for the function. + /// + /// On x86_64, this is always `DllCallingConvention::C`; on i686, it can be any + /// of the values, and we use `DllCallingConvention::C` to represent `"cdecl"`. + pub calling_convention: DllCallingConvention, + /// Span of import's "extern" declaration; used for diagnostics. + pub span: Span, +} + +/// Calling convention for a function defined in an external library. +/// +/// The usize value, where present, indicates the size of the function's argument list +/// in bytes. +#[derive(Copy, Clone, Debug, PartialEq, Eq, Encodable, Decodable, Hash, HashStable)] +pub enum DllCallingConvention { + C, + Stdcall(usize), + Fastcall(usize), + Vectorcall(usize), } #[derive(Clone, TyEncodable, TyDecodable, HashStable, Debug)] diff --git a/src/test/run-make/raw-dylib-alt-calling-convention/Makefile b/src/test/run-make/raw-dylib-alt-calling-convention/Makefile new file mode 100644 index 0000000000000..0f874333fa09c --- /dev/null +++ b/src/test/run-make/raw-dylib-alt-calling-convention/Makefile @@ -0,0 +1,18 @@ +# Test the behavior of #[link(.., kind = "raw-dylib")] with alternative calling conventions. + +# only-i686-pc-windows-msvc + +-include ../../run-make-fulldeps/tools.mk + +all: + $(call COMPILE_OBJ,"$(TMPDIR)"/extern.obj,extern.c) + $(CC) "$(TMPDIR)"/extern.obj -link -dll -out:"$(TMPDIR)"/extern.dll + $(RUSTC) --crate-type lib --crate-name raw_dylib_alt_calling_convention_test lib.rs + $(RUSTC) --crate-type bin driver.rs -L "$(TMPDIR)" + "$(TMPDIR)"/driver > "$(TMPDIR)"/output.txt + +ifdef RUSTC_BLESS_TEST + cp "$(TMPDIR)"/output.txt output.txt +else + $(DIFF) output.txt "$(TMPDIR)"/output.txt +endif diff --git a/src/test/run-make/raw-dylib-alt-calling-convention/driver.rs b/src/test/run-make/raw-dylib-alt-calling-convention/driver.rs new file mode 100644 index 0000000000000..3710507f5e44e --- /dev/null +++ b/src/test/run-make/raw-dylib-alt-calling-convention/driver.rs @@ -0,0 +1,5 @@ +extern crate raw_dylib_alt_calling_convention_test; + +fn main() { + raw_dylib_alt_calling_convention_test::library_function(); +} diff --git a/src/test/run-make/raw-dylib-alt-calling-convention/extern.c b/src/test/run-make/raw-dylib-alt-calling-convention/extern.c new file mode 100644 index 0000000000000..8f64abf2fb505 --- /dev/null +++ b/src/test/run-make/raw-dylib-alt-calling-convention/extern.c @@ -0,0 +1,123 @@ +#include +#include + +struct S { + uint8_t x; + int32_t y; +}; + +struct S2 { + int32_t x; + uint8_t y; +}; + +struct S3 { + uint8_t x[5]; +}; + +__declspec(dllexport) void __stdcall stdcall_fn_1(int i) { + printf("stdcall_fn_1(%d)\n", i); + fflush(stdout); +} + +__declspec(dllexport) void __stdcall stdcall_fn_2(uint8_t i, float f) { + printf("stdcall_fn_2(%d, %.1f)\n", i, f); + fflush(stdout); +} + +__declspec(dllexport) void __stdcall stdcall_fn_3(double d) { + printf("stdcall_fn_3(%.1f)\n", d); + fflush(stdout); +} + +__declspec(dllexport) void __stdcall stdcall_fn_4(uint8_t i, uint8_t j, float f) { + printf("stdcall_fn_4(%d, %d, %.1f)\n", i, j, f); + fflush(stdout); +} + +__declspec(dllexport) void __stdcall stdcall_fn_5(struct S s, int i) { + printf("stdcall_fn_5(S { x: %d, y: %d }, %d)\n", s.x, s.y, i); + fflush(stdout); +} + +// Test that stdcall support works correctly with the nullable pointer optimization. +__declspec(dllexport) void __stdcall stdcall_fn_6(struct S* s) { + if (s) { + printf("stdcall_fn_6(S { x: %d, y: %d })\n", s->x, s->y); + } else { + printf("stdcall_fn_6(null)\n"); + } + fflush(stdout); +} + +__declspec(dllexport) void __stdcall stdcall_fn_7(struct S2 s, int i) { + printf("stdcall_fn_7(S2 { x: %d, y: %d }, %d)\n", s.x, s.y, i); + fflush(stdout); +} + +// Verify that we compute the correct amount of space in the argument list for a 5-byte struct. +__declspec(dllexport) void __stdcall stdcall_fn_8(struct S3 s, struct S3 t) { + printf("stdcall_fn_8(S3 { x: [%d, %d, %d, %d, %d] }, S3 { x: [%d, %d, %d, %d, %d] })\n", + s.x[0], s.x[1], s.x[2], s.x[3], s.x[4], + t.x[0], t.x[1], t.x[2], t.x[3], t.x[4] + ); + fflush(stdout); +} + +// test whether f64/double values are aligned on 4-byte or 8-byte boundaries. +__declspec(dllexport) void __stdcall stdcall_fn_9(uint8_t x, double y) { + printf("stdcall_fn_9(%d, %.1f)\n", x, y); + fflush(stdout); +} + +__declspec(dllexport) void __fastcall fastcall_fn_1(int i) { + printf("fastcall_fn_1(%d)\n", i); + fflush(stdout); +} + +__declspec(dllexport) void __fastcall fastcall_fn_2(uint8_t i, float f) { + printf("fastcall_fn_2(%d, %.1f)\n", i, f); + fflush(stdout); +} + +__declspec(dllexport) void __fastcall fastcall_fn_3(double d) { + printf("fastcall_fn_3(%.1f)\n", d); + fflush(stdout); +} + +__declspec(dllexport) void __fastcall fastcall_fn_4(uint8_t i, uint8_t j, float f) { + printf("fastcall_fn_4(%d, %d, %.1f)\n", i, j, f); + fflush(stdout); +} + +__declspec(dllexport) void __fastcall fastcall_fn_5(struct S s, int i) { + printf("fastcall_fn_5(S { x: %d, y: %d }, %d)\n", s.x, s.y, i); + fflush(stdout); +} + +__declspec(dllexport) void __fastcall fastcall_fn_6(struct S* s) { + if (s) { + printf("fastcall_fn_6(S { x: %d, y: %d })\n", s->x, s->y); + } else { + printf("fastcall_fn_6(null)\n"); + } + fflush(stdout); +} + +__declspec(dllexport) void __fastcall fastcall_fn_7(struct S2 s, int i) { + printf("fastcall_fn_7(S2 { x: %d, y: %d }, %d)\n", s.x, s.y, i); + fflush(stdout); +} + +__declspec(dllexport) void __fastcall fastcall_fn_8(struct S3 s, struct S3 t) { + printf("fastcall_fn_8(S3 { x: [%d, %d, %d, %d, %d] }, S3 { x: [%d, %d, %d, %d, %d] })\n", + s.x[0], s.x[1], s.x[2], s.x[3], s.x[4], + t.x[0], t.x[1], t.x[2], t.x[3], t.x[4] + ); + fflush(stdout); +} + +__declspec(dllexport) void __fastcall fastcall_fn_9(uint8_t x, double y) { + printf("fastcall_fn_9(%d, %.1f)\n", x, y); + fflush(stdout); +} diff --git a/src/test/run-make/raw-dylib-alt-calling-convention/lib.rs b/src/test/run-make/raw-dylib-alt-calling-convention/lib.rs new file mode 100644 index 0000000000000..ba0f1418aba77 --- /dev/null +++ b/src/test/run-make/raw-dylib-alt-calling-convention/lib.rs @@ -0,0 +1,71 @@ +#![feature(raw_dylib)] + +#[repr(C)] +#[derive(Clone)] +struct S { + x: u8, + y: i32, +} + +#[repr(C)] +#[derive(Clone)] +struct S2 { + x: i32, + y: u8, +} + +#[repr(C)] +#[derive(Clone)] +struct S3 { + x: [u8; 5], +} + +#[link(name = "extern", kind = "raw-dylib")] +extern "stdcall" { + fn stdcall_fn_1(i: i32); + fn stdcall_fn_2(c: u8, f: f32); + fn stdcall_fn_3(d: f64); + fn stdcall_fn_4(i: u8, j: u8, f: f32); + fn stdcall_fn_5(a: S, b: i32); + fn stdcall_fn_6(a: Option<&S>); + fn stdcall_fn_7(a: S2, b: i32); + fn stdcall_fn_8(a: S3, b: S3); + fn stdcall_fn_9(x: u8, y: f64); +} + +#[link(name = "extern", kind = "raw-dylib")] +extern "fastcall" { + fn fastcall_fn_1(i: i32); + fn fastcall_fn_2(c: u8, f: f32); + fn fastcall_fn_3(d: f64); + fn fastcall_fn_4(i: u8, j: u8, f: f32); + fn fastcall_fn_5(a: S, b: i32); + fn fastcall_fn_6(a: Option<&S>); + fn fastcall_fn_7(a: S2, b: i32); + fn fastcall_fn_8(a: S3, b: S3); + fn fastcall_fn_9(x: u8, y: f64); +} + +pub fn library_function() { + unsafe { + stdcall_fn_1(14); + stdcall_fn_2(16, 3.5); + stdcall_fn_3(3.5); + stdcall_fn_4(1, 2, 3.0); + stdcall_fn_5(S { x: 1, y: 2 }, 16); + stdcall_fn_6(Some(&S { x: 10, y: 12 })); + stdcall_fn_7(S2 { x: 15, y: 16 }, 3); + stdcall_fn_8(S3 { x: [1, 2, 3, 4, 5] }, S3 { x: [6, 7, 8, 9, 10] }); + stdcall_fn_9(1, 3.0); + + fastcall_fn_1(14); + fastcall_fn_2(16, 3.5); + fastcall_fn_3(3.5); + fastcall_fn_4(1, 2, 3.0); + fastcall_fn_5(S { x: 1, y: 2 }, 16); + fastcall_fn_6(Some(&S { x: 10, y: 12 })); + fastcall_fn_7(S2 { x: 15, y: 16 }, 3); + fastcall_fn_8(S3 { x: [1, 2, 3, 4, 5] }, S3 { x: [6, 7, 8, 9, 10] }); + fastcall_fn_9(1, 3.0); + } +} diff --git a/src/test/run-make/raw-dylib-alt-calling-convention/output.txt b/src/test/run-make/raw-dylib-alt-calling-convention/output.txt new file mode 100644 index 0000000000000..be598a2202782 --- /dev/null +++ b/src/test/run-make/raw-dylib-alt-calling-convention/output.txt @@ -0,0 +1,18 @@ +stdcall_fn_1(14) +stdcall_fn_2(16, 3.5) +stdcall_fn_3(3.5) +stdcall_fn_4(1, 2, 3.0) +stdcall_fn_5(S { x: 1, y: 2 }, 16) +stdcall_fn_6(S { x: 10, y: 12 }) +stdcall_fn_7(S2 { x: 15, y: 16 }, 3) +stdcall_fn_8(S3 { x: [1, 2, 3, 4, 5] }, S3 { x: [6, 7, 8, 9, 10] }) +stdcall_fn_9(1, 3.0) +fastcall_fn_1(14) +fastcall_fn_2(16, 3.5) +fastcall_fn_3(3.5) +fastcall_fn_4(1, 2, 3.0) +fastcall_fn_5(S { x: 1, y: 2 }, 16) +fastcall_fn_6(S { x: 10, y: 12 }) +fastcall_fn_7(S2 { x: 15, y: 16 }, 3) +fastcall_fn_8(S3 { x: [1, 2, 3, 4, 5] }, S3 { x: [6, 7, 8, 9, 10] }) +fastcall_fn_9(1, 3.0) diff --git a/src/test/run-make/raw-dylib/Makefile b/src/test/run-make/raw-dylib-c/Makefile similarity index 96% rename from src/test/run-make/raw-dylib/Makefile rename to src/test/run-make/raw-dylib-c/Makefile index 7ce46fd93318b..26ab4d34764d1 100644 --- a/src/test/run-make/raw-dylib/Makefile +++ b/src/test/run-make/raw-dylib-c/Makefile @@ -1,7 +1,6 @@ # Test the behavior of #[link(.., kind = "raw-dylib")] on windows-msvc -# only-windows -# only-msvc +# only-windows-msvc -include ../../run-make-fulldeps/tools.mk diff --git a/src/test/run-make/raw-dylib/driver.rs b/src/test/run-make/raw-dylib-c/driver.rs similarity index 100% rename from src/test/run-make/raw-dylib/driver.rs rename to src/test/run-make/raw-dylib-c/driver.rs diff --git a/src/test/run-make/raw-dylib/extern_1.c b/src/test/run-make/raw-dylib-c/extern_1.c similarity index 100% rename from src/test/run-make/raw-dylib/extern_1.c rename to src/test/run-make/raw-dylib-c/extern_1.c diff --git a/src/test/run-make/raw-dylib/extern_2.c b/src/test/run-make/raw-dylib-c/extern_2.c similarity index 100% rename from src/test/run-make/raw-dylib/extern_2.c rename to src/test/run-make/raw-dylib-c/extern_2.c diff --git a/src/test/run-make/raw-dylib/lib.rs b/src/test/run-make/raw-dylib-c/lib.rs similarity index 100% rename from src/test/run-make/raw-dylib/lib.rs rename to src/test/run-make/raw-dylib-c/lib.rs diff --git a/src/test/run-make/raw-dylib/output.txt b/src/test/run-make/raw-dylib-c/output.txt similarity index 100% rename from src/test/run-make/raw-dylib/output.txt rename to src/test/run-make/raw-dylib-c/output.txt diff --git a/src/test/ui/rfc-2627-raw-dylib/multiple-definitions.rs b/src/test/ui/rfc-2627-raw-dylib/multiple-definitions.rs new file mode 100644 index 0000000000000..3f7597498baa4 --- /dev/null +++ b/src/test/ui/rfc-2627-raw-dylib/multiple-definitions.rs @@ -0,0 +1,19 @@ +// only-i686-pc-windows-msvc +// compile-flags: --crate-type lib --emit link +#![allow(clashing_extern_declarations)] +#![feature(raw_dylib)] +//~^ WARN the feature `raw_dylib` is incomplete +#[link(name = "foo", kind = "raw-dylib")] +extern "C" { + fn f(x: i32); + //~^ ERROR multiple definitions of external function `f` from library `foo.dll` have different calling conventions +} + +pub fn lib_main() { + #[link(name = "foo", kind = "raw-dylib")] + extern "stdcall" { + fn f(x: i32); + } + + unsafe { f(42); } +} diff --git a/src/test/ui/rfc-2627-raw-dylib/multiple-definitions.stderr b/src/test/ui/rfc-2627-raw-dylib/multiple-definitions.stderr new file mode 100644 index 0000000000000..91f6f0cf7220b --- /dev/null +++ b/src/test/ui/rfc-2627-raw-dylib/multiple-definitions.stderr @@ -0,0 +1,17 @@ +warning: the feature `raw_dylib` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/multiple-definitions.rs:4:12 + | +LL | #![feature(raw_dylib)] + | ^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #58713 for more information + +error: multiple definitions of external function `f` from library `foo.dll` have different calling conventions + --> $DIR/multiple-definitions.rs:8:5 + | +LL | fn f(x: i32); + | ^^^^^^^^^^^^^ + +error: aborting due to previous error; 1 warning emitted + diff --git a/src/test/ui/rfc-2627-raw-dylib/unsupported-abi.rs b/src/test/ui/rfc-2627-raw-dylib/unsupported-abi.rs new file mode 100644 index 0000000000000..e5a5ac2eb2bf1 --- /dev/null +++ b/src/test/ui/rfc-2627-raw-dylib/unsupported-abi.rs @@ -0,0 +1,13 @@ +// only-x86_64-pc-windows-msvc +// compile-flags: --crate-type lib --emit link +#![allow(incomplete_features)] +#![feature(raw_dylib)] +#[link(name = "foo", kind = "raw-dylib")] +extern "stdcall" { + fn f(x: i32); + //~^ ERROR ABI not supported by `#[link(kind = "raw-dylib")]` on this architecture +} + +pub fn lib_main() { + unsafe { f(42); } +} diff --git a/src/test/ui/rfc-2627-raw-dylib/unsupported-abi.stderr b/src/test/ui/rfc-2627-raw-dylib/unsupported-abi.stderr new file mode 100644 index 0000000000000..fc9008128ae43 --- /dev/null +++ b/src/test/ui/rfc-2627-raw-dylib/unsupported-abi.stderr @@ -0,0 +1,8 @@ +error: ABI not supported by `#[link(kind = "raw-dylib")]` on this architecture + --> $DIR/unsupported-abi.rs:7:5 + | +LL | fn f(x: i32); + | ^^^^^^^^^^^^^ + +error: aborting due to previous error +