1
Fork 0

Make RustString an extern type to avoid improper_ctypes warnings

This commit is contained in:
Zalathar 2024-11-03 12:40:26 +11:00
parent 730626dbd9
commit 89d7efaf8f
4 changed files with 58 additions and 46 deletions

View file

@ -104,8 +104,9 @@ typedef struct OpaqueRustString *RustStringRef;
typedef struct LLVMOpaqueTwine *LLVMTwineRef;
typedef struct LLVMOpaqueSMDiagnostic *LLVMSMDiagnosticRef;
extern "C" void LLVMRustStringWriteImpl(RustStringRef Str, const char *Ptr,
size_t Size);
extern "C" void LLVMRustStringWriteImpl(RustStringRef buf,
const char *slice_ptr,
size_t slice_len);
class RawRustStringOstream : public llvm::raw_ostream {
RustStringRef Str;

View file

@ -2,42 +2,75 @@
#![allow(internal_features)]
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
#![doc(rust_logo)]
#![feature(extern_types)]
#![feature(rustdoc_internals)]
#![warn(unreachable_pub)]
// tidy-alphabetical-end
// NOTE: This crate only exists to allow linking on mingw targets.
use std::cell::RefCell;
use std::slice;
use std::{ptr, slice};
use libc::{c_char, size_t};
use libc::size_t;
#[repr(C)]
pub struct RustString {
pub bytes: RefCell<Vec<u8>>,
unsafe extern "C" {
/// Opaque type that allows C++ code to write bytes to a Rust-side buffer,
/// in conjunction with `RawRustStringOstream`. Use this as `&RustString`
/// (Rust) and `RustStringRef` (C++) in FFI signatures.
pub type RustString;
}
impl RustString {
pub fn len(&self) -> usize {
self.bytes.borrow().len()
}
pub fn is_empty(&self) -> bool {
self.bytes.borrow().is_empty()
pub fn build_byte_buffer(closure: impl FnOnce(&Self)) -> Vec<u8> {
let buf = RustStringInner::default();
closure(buf.as_opaque());
buf.into_inner()
}
}
/// Appending to a Rust string -- used by RawRustStringOstream.
/// Underlying implementation of [`RustString`].
///
/// Having two separate types makes it possible to use the opaque [`RustString`]
/// in FFI signatures without `improper_ctypes` warnings. This is a workaround
/// for the fact that there is no way to opt out of `improper_ctypes` when
/// _declaring_ a type (as opposed to using that type).
#[derive(Default)]
struct RustStringInner {
bytes: RefCell<Vec<u8>>,
}
impl RustStringInner {
fn as_opaque(&self) -> &RustString {
let ptr: *const RustStringInner = ptr::from_ref(self);
// We can't use `ptr::cast` here because extern types are `!Sized`.
let ptr = ptr as *const RustString;
unsafe { &*ptr }
}
fn from_opaque(opaque: &RustString) -> &Self {
// SAFETY: A valid `&RustString` must have been created via `as_opaque`.
let ptr: *const RustString = ptr::from_ref(opaque);
let ptr: *const RustStringInner = ptr.cast();
unsafe { &*ptr }
}
fn into_inner(self) -> Vec<u8> {
self.bytes.into_inner()
}
}
/// Appends the contents of a byte slice to a [`RustString`].
///
/// This function is implemented in `rustc_llvm` so that the C++ code in this
/// crate can link to it directly, without an implied link-time dependency on
/// `rustc_codegen_llvm`.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn LLVMRustStringWriteImpl(
sr: &RustString,
ptr: *const c_char,
size: size_t,
buf: &RustString,
slice_ptr: *const u8, // Same ABI as `*const c_char`
slice_len: size_t,
) {
let slice = unsafe { slice::from_raw_parts(ptr as *const u8, size) };
sr.bytes.borrow_mut().extend_from_slice(slice);
let slice = unsafe { slice::from_raw_parts(slice_ptr, slice_len) };
RustStringInner::from_opaque(buf).bytes.borrow_mut().extend_from_slice(slice);
}
/// Initialize targets enabled by the build script via `cfg(llvm_component = "...")`.