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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
//! Functions in this group operate on composite constants.

use super::ValueRef;
use crate::core::context::ContextRef;
use crate::core::types::TypeRef;
use crate::{CInt, CStr, CString, CUint, GetRef};
use llvm_sys::core;

impl ValueRef {
    /// Create a `ConstantDataSequential` and initialize it with a string.
    ///
    /// # Details
    ///
    /// Creates a constant string value in a specified LLVM context.
    ///
    /// This function wraps the `LLVMConstStringInContext` function from the LLVM core library. It generates a constant
    /// string value within the specified LLVM context (`context`) using the provided string slice (`string`). The function
    /// also allows you to specify whether the string should be null-terminated.
    ///
    /// # Parameters
    ///
    /// - `context`: A reference to the LLVM context (`ContextRef`) in which the constant string should be created.
    /// - `string`: A string slice that represents the content of the constant string. This string will be used to generate the LLVM constant.
    /// - `dont_null_terminate`: A boolean value indicating whether the string should not be null-terminated. If `true`, the string will not be null-terminated; if `false`, a null terminator will be added.
    ///
    /// # Returns
    ///
    /// Returns an instance of `ValueRef`, which encapsulates the constant string value created in the specified context.
    #[must_use]
    pub fn const_string_in_context(
        context: &ContextRef,
        string: &str,
        dont_null_terminate: bool,
    ) -> Self {
        let c_string = CString::from(string);
        unsafe {
            Self(core::LLVMConstStringInContext(
                context.get_ref(),
                c_string.as_ptr(),
                *CUint::from(string.len()),
                *CInt::from(dont_null_terminate),
            ))
        }
    }

    /// Create a `ConstantDataSequential` with string content in the global context.
    ///
    /// This is the same as `const_string_in_context` except it operates on the
    /// global context.
    ///
    /// # Details
    ///
    /// Creates a constant string value in the global LLVM context.
    ///
    /// This function wraps the `LLVMConstString` function from the LLVM core library. It generates a constant
    /// string value within the global LLVM context using the provided string slice (`string`). The function
    /// also allows you to specify whether the string should be null-terminated. This function is similar to
    /// `const_string_in_context`, but it operates on the global context instead of a specified context.
    ///
    /// # Parameters
    ///
    /// - `string`: A string slice that represents the content of the constant string. This string will be used to generate the LLVM constant.
    /// - `dont_null_terminate`: A boolean value indicating whether the string should not be null-terminated. If `true`, the string will not be null-terminated; if `false`, a null terminator will be added.
    ///
    /// # Returns
    ///
    /// Returns an instance of `ValueRef`, which encapsulates the constant string value created in the global context.
    #[must_use]
    pub fn const_string(string: &str, dont_null_terminate: bool) -> Self {
        let c_string = CString::from(string);
        unsafe {
            Self(core::LLVMConstString(
                c_string.as_ptr(),
                *CUint::from(string.len()),
                *CInt::from(dont_null_terminate),
            ))
        }
    }

    /// Returns true if the specified constant is an array of `i8`.
    ///
    /// # Details
    ///
    /// Checks if the value is a constant string.
    ///
    /// This function wraps the `LLVMIsConstantString` function from the LLVM core library. It determines whether
    /// the value represented by `self` is a constant string. This is useful when you need to verify if a particular
    /// LLVM value is a constant string within the IR.
    ///
    /// # Returns
    ///
    /// Returns `true` if the value is a constant string, otherwise returns `false`.
    #[must_use]
    pub fn is_constant_string(&self) -> bool {
        unsafe { core::LLVMIsConstantString(self.0) != 0 }
    }

    /// Get the given constant data sequential as a string.
    ///
    /// # Details
    ///
    /// Retrieves the value as a string if the value is a constant string.
    ///
    /// This function wraps the `LLVMGetAsString` function from the LLVM core library. It attempts to extract the value
    /// represented by `self` as a string if it is a constant string. The function returns `None` if the value is not a
    /// constant string, or `Some(String)` containing the string representation if it is.
    ///
    /// # Returns
    ///
    /// Returns an `Option<String>`:
    /// - `Some(String)` containing the string representation of the constant if it is a constant string.
    /// - `None` if the value is not a constant string.
    #[must_use]
    pub fn get_as_string(&self) -> Option<String> {
        unsafe {
            let mut length = 0;
            let c_str = core::LLVMGetAsString(self.0, &mut length);
            if c_str.is_null() {
                None
            } else {
                Some(CStr::new(c_str).to_string())
            }
        }
    }

    /// Create an anonymous `ConstantStruct` with the specified values.
    ///
    /// # Details
    ///
    /// Creates a constant struct value in a specified LLVM context.
    ///
    /// This function wraps the `LLVMConstStructInContext` function from the LLVM core library. It generates a constant
    /// struct value within the specified LLVM context (`context`) using an array of constant values (`constant_vals`).
    /// The `packed` parameter allows you to specify whether the struct should be packed, meaning that its fields are
    /// laid out without padding.
    ///
    /// # Parameters
    ///
    /// - `context`: A reference to the LLVM context (`ContextRef`) in which the constant struct should be created.
    /// - `constant_vals`: A slice of constant values (`&[Self]`) that will be used as the fields of the struct. Each element in this slice corresponds to a field in the struct.
    /// - `packed`: A boolean value indicating whether the struct should be packed (`true` for packed, `false` for unpacked). A packed struct has its fields tightly packed without padding.
    ///
    /// # Returns
    ///
    /// Returns an instance of `ValueRef`, which encapsulates the constant struct value created in the specified context.
    #[must_use]
    pub fn const_struct_in_context(
        context: &ContextRef,
        constant_vals: &[Self],
        packed: bool,
    ) -> Self {
        let mut constant_vals = constant_vals.iter().map(|v| v.0).collect::<Vec<_>>();
        let constant_vals_ptr = if constant_vals.is_empty() {
            std::ptr::null_mut()
        } else {
            constant_vals.as_mut_ptr()
        };
        unsafe {
            Self(core::LLVMConstStructInContext(
                context.get_ref(),
                constant_vals_ptr,
                *CUint::from(constant_vals.len()),
                *CInt::from(packed),
            ))
        }
    }

    /// Create a `ConstantStruct` in the global `Context`.
    ///
    /// This is the same as `constStruct_in_context` except it operates on the
    /// global context.
    ///
    /// # Details
    ///
    /// Creates a constant struct value in the global LLVM context.
    ///
    /// This function wraps the `LLVMConstStruct` function from the LLVM core library. It generates a constant
    /// struct value using an array of constant values (`constant_vals`). The `packed` parameter allows you to specify
    /// whether the struct should be packed, meaning that its fields are laid out without padding. This function operates
    /// in the global LLVM context, as opposed to a specific context.
    ///
    /// # Parameters
    ///
    /// - `constant_vals`: A slice of constant values (`&[Self]`) that will be used as the fields of the struct. Each element in this slice corresponds to a field in the struct.
    /// - `packed`: A boolean value indicating whether the struct should be packed (`true` for packed, `false` for unpacked). A packed struct has its fields tightly packed without padding.
    ///
    /// # Returns
    ///
    /// Returns an instance of `ValueRef`, which encapsulates the constant struct value created in the global context.
    #[must_use]
    pub fn const_struct(constant_vals: &[Self], packed: bool) -> Self {
        let mut constant_vals = constant_vals.iter().map(|v| v.0).collect::<Vec<_>>();
        let constant_vals_ptr = if constant_vals.is_empty() {
            std::ptr::null_mut()
        } else {
            constant_vals.as_mut_ptr()
        };
        unsafe {
            Self(core::LLVMConstStruct(
                constant_vals_ptr,
                *CUint::from(constant_vals.len()),
                *CInt::from(packed),
            ))
        }
    }

    /// Create a `ConstantArray` from values.
    ///
    /// # Details
    ///
    /// Creates a constant array value with elements of a specified type.
    ///
    /// This function wraps the `LLVMConstArray2` function from the LLVM core library. It generates a constant
    /// array value with the specified element type (`element_type`) using an array of constant values (`constant_vals`).
    /// Each element in `constant_vals` must be of the same type as `element_type`.
    ///
    /// # Parameters
    ///
    /// - `element_type`: A reference to the type of elements in the array (`TypeRef`). This specifies the type that each element in the array should have.
    /// - `constant_vals`: A slice of constant values (`&[Self]`) that will be used as the elements of the array. Each element in this slice corresponds to an element in the resulting array.
    ///
    /// # Returns
    ///
    /// Returns an instance of `ValueRef`, which encapsulates the constant array value created with the specified element type and elements.
    #[must_use]
    pub fn const_array2(element_type: &TypeRef, constant_vals: &[Self]) -> Self {
        let mut constant_vals = constant_vals.iter().map(|v| v.0).collect::<Vec<_>>();
        let constant_vals_ptr = if constant_vals.is_empty() {
            std::ptr::null_mut()
        } else {
            constant_vals.as_mut_ptr()
        };
        unsafe {
            Self(core::LLVMConstArray2(
                element_type.get_ref(),
                constant_vals_ptr,
                u64::try_from(constant_vals.len()).unwrap_or(u64::MAX),
            ))
        }
    }

    /// Create a non-anonymous `ConstantStruct` from values.
    ///
    /// # Details
    ///
    /// Creates a constant named struct value with specified field values.
    ///
    /// This function wraps the `LLVMConstNamedStruct` function from the LLVM core library. It generates a constant
    /// struct value of the specified named struct type (`struct_type`) using an array of constant values (`constant_vals`).
    /// Each element in `constant_vals` corresponds to a field in the struct.
    ///
    /// # Parameters
    ///
    /// - `struct_type`: A reference to the named struct type (`TypeRef`) for the constant value. This type specifies the structure that the constant will represent.
    /// - `constant_vals`: A slice of constant values (`&[Self]`) that will be used as the fields of the struct. Each element in this slice corresponds to a field in the struct.
    ///
    /// # Returns
    ///
    /// Returns an instance of `ValueRef`, which encapsulates the constant named struct value created with the specified fields.
    #[must_use]
    pub fn const_named_struct(struct_type: &TypeRef, constant_vals: &[Self]) -> Self {
        let mut constant_vals = constant_vals.iter().map(|v| v.0).collect::<Vec<_>>();
        let constant_vals_ptr = if constant_vals.is_empty() {
            std::ptr::null_mut()
        } else {
            constant_vals.as_mut_ptr()
        };
        unsafe {
            Self(core::LLVMConstNamedStruct(
                struct_type.get_ref(),
                constant_vals_ptr,
                *CUint::from(constant_vals.len()),
            ))
        }
    }

    /// Get element of a constant aggregate `(struct, array or vector)` at the
    /// specified index. Returns `None` if the index is out of range, or it's not
    /// possible to determine the element (e.g., because the constant is a
    /// constant expression.)
    ///
    /// # Details
    ///
    /// Retrieves a specific element from an aggregate constant (e.g., an array or struct).
    ///
    /// This function wraps the `LLVMGetAggregateElement` function from the LLVM core library. It returns the element
    /// at the specified index (`idx`) from the aggregate constant represented by `self`. If the index is out of bounds
    /// or the element cannot be retrieved, the function returns `None`.
    ///
    /// # Parameters
    ///
    /// - `idx`: The index of the element to retrieve from the aggregate constant. This index should be within the bounds of the aggregate's elements.
    ///
    /// # Returns
    ///
    /// Returns an `Option<ValueRef>`:
    /// - `Some(ValueRef)` containing the retrieved element if the index is valid and the element is found.
    /// - `None` if the index is out of bounds or the element cannot be retrieved.
    #[must_use]
    pub fn get_aggregate_element(&self, idx: u32) -> Option<Self> {
        let element = unsafe { core::LLVMGetAggregateElement(self.0, *CUint::from(idx)) };
        if element.is_null() {
            None
        } else {
            Some(Self(element))
        }
    }

    /// Create a `ConstantVector` from values.
    ///
    /// # Details
    ///
    /// Creates a constant vector value from an array of scalar constant values.
    ///
    /// This function wraps the `LLVMConstVector` function from the LLVM core library. It generates a constant
    /// vector using the provided array of scalar constant values (`scalar_constant_vals`). Each element in the array
    /// corresponds to an element in the resulting vector, and the type of the vector is inferred from the types of the scalar constants.
    ///
    /// # Parameters
    ///
    /// - `scalar_constant_vals`: A slice of scalar constant values (`&[Self]`) that will be used as the elements of the vector. Each element in this slice corresponds to an element in the resulting vector.
    ///
    /// # Returns
    ///
    /// Returns an instance of `ValueRef`, which encapsulates the constant vector value created from the specified scalar constants.
    #[must_use]
    pub fn const_vector(scalar_constant_vals: &[Self]) -> Self {
        let mut scalar_constant_vals = scalar_constant_vals.iter().map(|v| v.0).collect::<Vec<_>>();
        let scalar_constant_vals_ptr = if scalar_constant_vals.is_empty() {
            std::ptr::null_mut()
        } else {
            scalar_constant_vals.as_mut_ptr()
        };
        unsafe {
            Self(core::LLVMConstVector(
                scalar_constant_vals_ptr,
                *CUint::from(scalar_constant_vals.len()),
            ))
        }
    }
}