Skip to main content

inkwell/
attributes.rs

1//! `Attribute`s are optional modifiers to functions, function parameters, and return types.
2
3use llvm_sys::core::{
4    LLVMGetEnumAttributeKind, LLVMGetEnumAttributeKindForName, LLVMGetEnumAttributeValue, LLVMGetLastEnumAttributeKind,
5    LLVMGetStringAttributeKind, LLVMGetStringAttributeValue, LLVMIsEnumAttribute, LLVMIsStringAttribute,
6};
7#[llvm_versions(12..)]
8use llvm_sys::core::{LLVMGetTypeAttributeValue, LLVMIsTypeAttribute};
9use llvm_sys::prelude::LLVMAttributeRef;
10
11use std::ffi::CStr;
12
13#[llvm_versions(12..)]
14use crate::types::AnyTypeEnum;
15
16// SubTypes: Attribute<Enum>, Attribute<String>
17// REVIEW: Should Attributes have a 'ctx lifetime?
18/// Functions, function parameters, and return types can have `Attribute`s to indicate
19/// how they should be treated by optimizations and code generation.
20#[derive(Clone, Copy)]
21pub struct Attribute {
22    pub(crate) attribute: LLVMAttributeRef,
23}
24
25impl std::fmt::Debug for Attribute {
26    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
27        if self.is_string() {
28            return f
29                .debug_struct("Attribute::String")
30                .field("ptr", &self.attribute)
31                .field("kind_id", &self.get_string_kind_id())
32                .field("value", &self.get_string_value())
33                .finish();
34        }
35
36        if self.is_enum() {
37            return f
38                .debug_struct("Attribute::Enum")
39                .field("ptr", &self.attribute)
40                .field("kind_id", &self.get_enum_kind_id())
41                .field("value", &self.get_enum_value())
42                .finish();
43        }
44
45        if self.is_type() {
46            return f
47                .debug_struct("Attribute::Type")
48                .field("ptr", &self.attribute)
49                .field("kind_id", &self.get_enum_kind_id())
50                .field("value", &self.get_type_value())
51                .finish();
52        }
53
54        unreachable!(
55            "attribute at {:?} is not a string, enum or type attribute",
56            self.attribute
57        );
58    }
59}
60
61impl Eq for Attribute {}
62
63impl PartialEq<Self> for Attribute {
64    fn eq(&self, other: &Self) -> bool {
65        if self.is_enum() && other.is_enum() {
66            return self.get_enum_kind_id() == other.get_enum_kind_id()
67                && self.get_enum_value() == other.get_enum_value();
68        }
69
70        if self.is_string() && other.is_string() {
71            return self.get_string_kind_id() == other.get_string_kind_id()
72                && self.get_string_value() == other.get_string_value();
73        }
74
75        if self.is_type() && other.is_type() {
76            // Seems to be some clippy bug here, but it's not clear why.
77            #[allow(clippy::unit_cmp)]
78            return self.get_enum_kind_id() == other.get_enum_kind_id()
79                && self.get_type_value() == other.get_type_value();
80        }
81
82        self.attribute == other.attribute
83    }
84}
85
86impl Attribute {
87    /// Creates a new `Attribute` from a raw pointer.
88    pub unsafe fn new(attribute: LLVMAttributeRef) -> Self {
89        debug_assert!(!attribute.is_null());
90
91        Attribute { attribute }
92    }
93
94    /// Acquires the underlying raw pointer belonging to this `Attribute` type.
95    pub fn as_mut_ptr(&self) -> LLVMAttributeRef {
96        self.attribute
97    }
98
99    /// Determines whether or not an `Attribute` is an enum. This method will
100    /// likely be removed in the future in favor of `Attribute`s being generically
101    /// defined.
102    ///
103    /// # Example
104    ///
105    /// ```no_run
106    /// use inkwell::context::Context;
107    ///
108    /// let context = Context::create();
109    /// let enum_attribute = context.create_enum_attribute(0, 10);
110    ///
111    /// assert!(enum_attribute.is_enum());
112    /// ```
113    pub fn is_enum(self) -> bool {
114        unsafe { LLVMIsEnumAttribute(self.attribute) == 1 }
115    }
116
117    /// Determines whether or not an `Attribute` is a string. This method will
118    /// likely be removed in the future in favor of `Attribute`s being generically
119    /// defined.
120    ///
121    /// # Example
122    ///
123    /// ```no_run
124    /// use inkwell::context::Context;
125    ///
126    /// let context = Context::create();
127    /// let string_attribute = context.create_string_attribute("my_key_123", "my_val");
128    ///
129    /// assert!(string_attribute.is_string());
130    /// ```
131    pub fn is_string(self) -> bool {
132        unsafe { LLVMIsStringAttribute(self.attribute) == 1 }
133    }
134
135    /// Determines whether or not an `Attribute` is a type attribute. This method will
136    /// likely be removed in the future in favor of `Attribute`s being generically
137    /// defined.
138    ///
139    /// # Example
140    ///
141    /// ```no_run
142    /// use inkwell::context::Context;
143    /// use inkwell::attributes::Attribute;
144    ///
145    /// let context = Context::create();
146    /// let kind_id = Attribute::get_named_enum_kind_id("sret");
147    /// let type_attribute = context.create_type_attribute(
148    ///     kind_id,
149    ///     context.i32_type().into(),
150    /// );
151    ///
152    /// assert!(type_attribute.is_type());
153    /// ```
154    #[llvm_versions(12..)]
155    pub fn is_type(self) -> bool {
156        unsafe { LLVMIsTypeAttribute(self.attribute) == 1 }
157    }
158
159    // private function to make code elsewhere easier
160    #[llvm_versions(..12)]
161    fn is_type(self) -> bool {
162        false
163    }
164
165    /// Gets the enum kind id associated with a builtin name.
166    ///
167    /// # Example
168    ///
169    /// ```no_run
170    /// use inkwell::attributes::Attribute;
171    ///
172    /// // This kind id doesn't exist:
173    /// assert_eq!(Attribute::get_named_enum_kind_id("foobar"), 0);
174    ///
175    /// // These are real kind ids:
176    /// assert_eq!(Attribute::get_named_enum_kind_id("align"), 1);
177    /// assert_eq!(Attribute::get_named_enum_kind_id("builtin"), 5);
178    /// ```
179    pub fn get_named_enum_kind_id(name: &str) -> u32 {
180        unsafe { LLVMGetEnumAttributeKindForName(name.as_ptr() as *const ::libc::c_char, name.len()) }
181    }
182
183    /// Gets the kind id associated with an enum `Attribute`.
184    ///
185    /// # Example
186    ///
187    /// ```no_run
188    /// use inkwell::context::Context;
189    ///
190    /// let context = Context::create();
191    /// let enum_attribute = context.create_enum_attribute(0, 10);
192    ///
193    /// assert_eq!(enum_attribute.get_enum_kind_id(), 0);
194    /// ```
195    #[cfg(feature = "llvm11-0")]
196    pub fn get_enum_kind_id(self) -> u32 {
197        assert!(self.get_enum_kind_id_is_valid()); // FIXME: SubTypes
198
199        unsafe { LLVMGetEnumAttributeKind(self.attribute) }
200    }
201
202    /// Gets the kind id associated with an enum `Attribute`.
203    ///
204    /// # Example
205    ///
206    /// ```no_run
207    /// use inkwell::context::Context;
208    ///
209    /// let context = Context::create();
210    /// let enum_attribute = context.create_enum_attribute(0, 10);
211    ///
212    /// assert_eq!(enum_attribute.get_enum_kind_id(), 0);
213    /// ```
214    ///
215    /// This function also works for type `Attribute`s.
216    ///
217    /// ```no_run
218    /// use inkwell::context::Context;
219    /// use inkwell::attributes::Attribute;
220    /// use inkwell::types::AnyType;
221    ///
222    /// let context = Context::create();
223    /// let kind_id = Attribute::get_named_enum_kind_id("sret");
224    /// let any_type = context.i32_type().as_any_type_enum();
225    /// let type_attribute = context.create_type_attribute(
226    ///     kind_id,
227    ///     any_type,
228    /// );
229    ///
230    /// assert_eq!(type_attribute.get_enum_kind_id(), kind_id);
231    /// ```
232    #[llvm_versions(12..)]
233    pub fn get_enum_kind_id(self) -> u32 {
234        assert!(self.get_enum_kind_id_is_valid()); // FIXME: SubTypes
235
236        unsafe { LLVMGetEnumAttributeKind(self.attribute) }
237    }
238
239    #[cfg(feature = "llvm11-0")]
240    fn get_enum_kind_id_is_valid(self) -> bool {
241        self.is_enum()
242    }
243
244    #[llvm_versions(12..)]
245    fn get_enum_kind_id_is_valid(self) -> bool {
246        self.is_enum() || self.is_type()
247    }
248
249    /// Gets the last enum kind id associated with builtin names.
250    ///
251    /// # Example
252    ///
253    /// ```no_run
254    /// use inkwell::attributes::Attribute;
255    ///
256    /// assert_eq!(Attribute::get_last_enum_kind_id(), 56);
257    /// ```
258    pub fn get_last_enum_kind_id() -> u32 {
259        unsafe { LLVMGetLastEnumAttributeKind() }
260    }
261
262    /// Gets the value associated with an enum `Attribute`.
263    ///
264    /// # Example
265    ///
266    /// ```no_run
267    /// use inkwell::context::Context;
268    ///
269    /// let context = Context::create();
270    /// let enum_attribute = context.create_enum_attribute(0, 10);
271    ///
272    /// assert_eq!(enum_attribute.get_enum_value(), 10);
273    /// ```
274    pub fn get_enum_value(self) -> u64 {
275        assert!(self.is_enum()); // FIXME: SubTypes
276
277        unsafe { LLVMGetEnumAttributeValue(self.attribute) }
278    }
279
280    /// Gets the string kind id associated with a string attribute.
281    ///
282    /// # Example
283    ///
284    /// ```no_run
285    /// use inkwell::context::Context;
286    ///
287    /// let context = Context::create();
288    /// let string_attribute = context.create_string_attribute("my_key", "my_val");
289    ///
290    /// assert_eq!(string_attribute.get_string_kind_id().to_str(), Ok("my_key"));
291    /// ```
292    // TODO: Check if null, return option
293    pub fn get_string_kind_id(&self) -> &CStr {
294        assert!(self.is_string()); // FIXME: SubTypes
295
296        let mut length = 0;
297        let cstr_ptr = unsafe { LLVMGetStringAttributeKind(self.attribute, &mut length) };
298
299        unsafe { CStr::from_ptr(cstr_ptr) }
300    }
301
302    /// Gets the string value associated with a string attribute.
303    ///
304    /// # Example
305    ///
306    /// ```no_run
307    /// use inkwell::context::Context;
308    ///
309    /// let context = Context::create();
310    /// let string_attribute = context.create_string_attribute("my_key", "my_val");
311    ///
312    /// assert_eq!(string_attribute.get_string_value().to_str(), Ok("my_val"));
313    /// ```
314    pub fn get_string_value(&self) -> &CStr {
315        assert!(self.is_string()); // FIXME: SubTypes
316
317        let mut length = 0;
318        let cstr_ptr = unsafe { LLVMGetStringAttributeValue(self.attribute, &mut length) };
319
320        unsafe { CStr::from_ptr(cstr_ptr) }
321    }
322
323    /// Gets the type associated with a type attribute.
324    ///
325    /// # Example
326    ///
327    /// ```no_run
328    /// use inkwell::context::Context;
329    /// use inkwell::attributes::Attribute;
330    /// use inkwell::types::AnyType;
331    ///
332    /// let context = Context::create();
333    /// let kind_id = Attribute::get_named_enum_kind_id("sret");
334    /// let any_type = context.i32_type().as_any_type_enum();
335    /// let type_attribute = context.create_type_attribute(
336    ///     kind_id,
337    ///     any_type,
338    /// );
339    ///
340    /// assert!(type_attribute.is_type());
341    /// assert_eq!(type_attribute.get_type_value(), any_type);
342    /// assert_ne!(type_attribute.get_type_value(), context.i64_type().as_any_type_enum());
343    /// ```
344    #[llvm_versions(12..)]
345    pub fn get_type_value(&self) -> AnyTypeEnum<'_> {
346        assert!(self.is_type()); // FIXME: SubTypes
347
348        unsafe { AnyTypeEnum::new(LLVMGetTypeAttributeValue(self.attribute)) }
349    }
350
351    // private function to make code elsewhere easier
352    #[llvm_versions(..12)]
353    fn get_type_value(&self) {
354        unreachable!("not implemented in this version")
355    }
356}
357
358/// An `AttributeLoc` determines where on a function an attribute is assigned to.
359#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
360pub enum AttributeLoc {
361    /// Assign to the `FunctionValue`'s return type.
362    Return,
363    /// Assign to one of the `FunctionValue`'s params (0-indexed).
364    Param(u32),
365    /// Assign to the `FunctionValue` itself.
366    Function,
367}
368
369impl AttributeLoc {
370    pub(crate) fn get_index(self) -> u32 {
371        match self {
372            AttributeLoc::Return => 0,
373            AttributeLoc::Param(index) => {
374                assert!(index <= u32::MAX - 2, "Param index must be <= u32::MAX - 2");
375                index + 1
376            },
377            AttributeLoc::Function => u32::MAX,
378        }
379    }
380}