1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
use std::{
any::Any,
ffi::{CStr, c_void},
marker::PhantomData,
pin::Pin,
ptr,
rc::Rc,
};
use llvm_sys::{
core::{
LLVMContextCreate, LLVMContextDispose, LLVMContextSetDiagnosticHandler,
LLVMGetDiagInfoDescription, LLVMGetDiagInfoSeverity, LLVMModuleCreateWithNameInContext,
},
prelude::{LLVMContextRef, LLVMDiagnosticInfoRef},
};
use crate::llvm::{LLVMDiagnosticHandler, Message, types::module::LLVMModule};
pub(crate) struct LLVMContext {
context: LLVMContextRef,
/// Optional diagnostic handler set for the context.
///
/// The diagnostic handler pointer must remain valid until either
/// a new handler is installed or the context is disposed.
/// To guarantee this, we keep a strong reference to the handler
/// inside the wrapper.
/// The type of the diagnostic handler is erased to make the
/// context wrapper non generic.
diagnostic_handler: Option<StoredHandler>,
}
impl LLVMContext {
pub(crate) fn new() -> Self {
let context = unsafe { LLVMContextCreate() };
Self {
context,
diagnostic_handler: None,
}
}
/// Returns an unsafe mutable pointer to the LLVM context.
///
/// The caller must ensure that the [`LLVMContext`] outlives the pointer this
/// function returns, or else it will end up dangling.
pub(in crate::llvm) const fn as_mut_ptr(&self) -> LLVMContextRef {
self.context
}
pub(crate) fn create_module<'ctx>(&'ctx self, name: &CStr) -> Option<LLVMModule<'ctx>> {
let module = unsafe { LLVMModuleCreateWithNameInContext(name.as_ptr(), self.context) };
if module.is_null() {
return None;
}
Some(LLVMModule {
module,
_marker: PhantomData,
})
}
/// Install a context-local diagnostic handler.
pub(crate) fn set_diagnostic_handler<T>(&mut self, handler: T) -> InstalledDiagnosticHandler<T>
where
T: LLVMDiagnosticHandler + 'static,
{
// Heap-allocate and pin the handler so its address is stable
// for the C API
let pinrc = Rc::pin(handler);
// Get a opaque raw pointer to the new memory stable object
let handler_ptr = ptr::from_ref(Pin::as_ref(&pinrc).get_ref()) as *mut c_void;
unsafe {
LLVMContextSetDiagnosticHandler(
self.context,
Some(diagnostic_handler::<T>),
handler_ptr,
)
};
// Keep the handler alive for at least as long as the context
// by storing a type-erased pinned clone in the context. This
// guards against the handler being dropped while LLVM still
// holds the callback pointer.
self.diagnostic_handler = Some(StoredHandler {
_handler: pinrc.clone(),
});
// Return a typed handle that keeps a strong, pinned reference to `T`.
//
// This lets the caller interact with the installed diagnostic handler
// directly (via `with_view`) without needing to query the context or
// deal with an Option. It also contributes to keeping the handler alive
// for as long as the handle (or the context-held clone) exists.
InstalledDiagnosticHandler { inner: pinrc }
}
}
impl Drop for LLVMContext {
fn drop(&mut self) {
unsafe {
LLVMContextDispose(self.context);
}
}
}
struct StoredHandler {
_handler: Pin<Rc<dyn Any>>,
}
#[derive(Clone)]
pub(crate) struct InstalledDiagnosticHandler<T: LLVMDiagnosticHandler> {
inner: Pin<Rc<T>>,
}
impl<T: LLVMDiagnosticHandler> InstalledDiagnosticHandler<T> {
pub(crate) fn with_view<R, F: FnOnce(&T) -> R>(&self, f: F) -> R {
f(Pin::as_ref(&self.inner).get_ref())
}
}
extern "C" fn diagnostic_handler<T: LLVMDiagnosticHandler>(
info: LLVMDiagnosticInfoRef,
handler: *mut c_void,
) {
let severity = unsafe { LLVMGetDiagInfoSeverity(info) };
let message = Message {
ptr: unsafe { LLVMGetDiagInfoDescription(info) },
};
let handler = handler.cast::<T>();
unsafe { &mut *handler }.handle_diagnostic(severity, message.as_string_lossy());
}