// ====================================================================
// StructType
// ====================================================================
///|
/// Struct Type
///
/// - See LLVM: `StructType::get`.
///
/// ```mbt check
/// test {
/// let ctx = Context::new()
/// let i8ty = ctx.getInt8Ty()
/// let i16ty = ctx.getInt16Ty()
/// let i32ty = ctx.getInt32Ty()
/// let f32ty = ctx.getFloatTy()
/// let sty = ctx.getStructType([i32ty, f32ty], name="foo")
/// inspect(sty.full_info(), content="%foo = type { i32, float }")
/// let sty = ctx.getStructType([i8ty, i16ty], name="bar", isPacked=true)
/// inspect(sty.full_info(), content="%bar = type <{ i8, i16 }>")
/// }
/// ```
pub struct StructType {
ctx : Context
priv mut name : String?
elements : Array[&Type]
priv mut isPacked : Bool
priv mut isSized : Bool?
//priv mut containsScalableVector: Bool
//priv mut containsNonGlobalTargetExtType: Bool
//priv mut containsNonLocalTargetExtType: Bool
} derive(Hash)
///|
/// Create a StructType.
/// FIXME: this is may different from LLVM Cpp
fn StructType::new(
ctx : Context,
elements : Array[&Type],
name~ : String?,
isPacked~ : Bool,
) -> StructType raise LLVMTypeError {
if name is None && elements.is_empty() {
raise OpaqueStructMustHaveName
}
let elements = elements.copy()
let namedStructTypes = ctx.pimpl.unwrap().namedStructTypes
let name = match name {
Some("") => raise InValidStructName("")
Some(n) if namedStructTypes.contains(n) => raise DuplicateStructName(n)
Some(n) => Some(n)
None => None
}
//let containsScalableVector = false
//let containsNonGlobalTargetExtType = false
//let containsNonLocalTargetExtType = false
let sty = StructType::{ ctx, name, elements, isPacked, isSized: None }
//containsScalableVector,
//containsNonGlobalTargetExtType,
//containsNonLocalTargetExtType
if name is Some(n) {
ctx.pimpl.unwrap().namedStructTypes.set(n, sty)
}
sty
}
///|
/// Check struct is a literal type.
///
/// literal type means the struct only has body but has no name.
///
/// - See LLVM: `StructType::isLiteral`.
///
/// ```mbt check
/// test {
/// let ctx = Context::new()
/// let i32ty = ctx.getInt32Ty()
/// let f32ty = ctx.getFloatTy()
/// let foo = ctx.getStructType([i32ty, f32ty])
/// let bar = ctx.getStructType([i32ty, f32ty], name="bar")
/// assert_true(foo.isLiteral())
/// assert_false(bar.isLiteral())
/// }
/// ```
pub fn StructType::isLiteral(self : StructType) -> Bool {
self.name is None
}
///|
/// Check struct is a opaque type.
/// Return true if this is a type with an identity that has no body specified
/// yet. These prints as 'opaque' in .ll files.
///
/// - See LLVM: `StructType::isOpaque`.
///
/// ```mbt check
/// test {
/// let ctx = Context::new()
/// let i32ty = ctx.getInt32Ty()
/// let f32ty = ctx.getFloatTy()
/// let foo = ctx.getStructType([], name="foo")
/// let bar = ctx.getStructType([i32ty, f32ty], name="bar")
/// assert_true(foo.isOpaque())
/// assert_false(bar.isOpaque())
/// }
/// ```
pub fn StructType::isOpaque(self : StructType) -> Bool {
self.elements.is_empty()
}
///|
/// Check if this is a packed struct type.
///
/// - See LLVM: `StructType::isPacked`.
///
/// Packed struct means the struct has no padding between its elements.
pub fn StructType::isPacked(self : StructType) -> Bool {
self.isPacked
}
///|
pub fn StructType::isSized(self : StructType) -> Bool {
if self.isSized is Some(sized) {
return sized
}
if self.isOpaque() {
return false
}
if self.elements().iter().any(ty => ty.isScalableTy() || not(ty.isSized())) {
return false
}
self.isSized = Some(true)
true
}
///|
/// Get the elements of the struct.
///
/// - See LLVM: `StructType::elements`.
pub fn StructType::elements(self : StructType) -> Array[&Type] {
self.elements
}
///|
///
/// - See LLVM: `StructType::element_begin` and `StructType::element_end`.
pub fn StructType::element_iter(self : StructType) -> Iter[&Type] {
self.elements.iter()
}
///|
/// Set the body of the struct.
///
/// Only Opaque struct can be set body.
///
/// - See LLVM: `StructType::setBody`.
pub fn StructType::setBody(
self : StructType,
elements : Array[&Type],
isPacked? : Bool = false,
) -> Unit raise LLVMTypeError {
// only opaque struct can be set body.
guard self.isOpaque() else { raise SetBodyForNonOpaqueStruct }
// No recursive struct.
for element in elements {
if element.asTypeEnum() is StructType(sty) && sty == self {
raise RecursiveStructDefinition
}
}
self.isPacked = isPacked
self.elements.clear()
elements.each(ty => self.elements.push(ty))
}
///|
/// Get the name of the struct.
///
/// If the struct is literal, return None.
///
/// - See LLVM: `StructType::getName`.
pub fn StructType::getName(self : StructType) -> String? {
self.name
}
///|
/// Set the name of the struct.
///
/// **Note**:
///
/// - If the new_name is "" (empty string), the struct will be a literal type.
/// - If the new_name has same name with old name, nothing will be changed.
/// - If context has already have a struct with the given new name, it will raise error.
///
/// - See LLVM: `StructType::setName`.
pub fn StructType::setName(
self : StructType,
new_name : String,
) -> Unit raise LLVMTypeError {
if new_name.is_empty() {
raise StructTypeNameCannotBeEmpty
}
let ctx = self.getContext().pimpl.unwrap()
let namedStructTypes = ctx.namedStructTypes
match (self.name, new_name) {
(Some(old_name), new_name) if old_name == new_name => return
(_, new_name) if namedStructTypes.contains(new_name) =>
raise DuplicateStructName(new_name)
(None, new_name) => {
self.name = Some(new_name)
namedStructTypes.set(new_name, self)
}
(Some(old_name), new_name) => {
namedStructTypes..remove(old_name)..set(new_name, self)
self.name = Some(new_name)
}
}
}
///|
pub fn StructType::removeName(self : StructType) -> Unit {
guard self.name is Some(name) else { return }
let ctx = self.getContext().pimpl.unwrap()
ctx.namedStructTypes.remove(name)
self.name = None
}
///|
pub fn StructType::getIndexedType(self : Self, idxs : ArrayView[Int]) -> &Type? {
guard self.elements.get(idxs[0]) is Some(ty) else { return None }
if idxs.length() == 1 {
return Some(ty)
}
if ty.tryAsAggregateTypeEnum() is Some(aggTy) {
aggTy.getIndexedType(idxs[1:])
} else {
None
}
}
///|
pub fn StructType::body_str(self : Self) -> String {
let s = self.elements.iter().map(ty => ty.to_string()).join(", ")
match self.isPacked() {
true => "<{ \{s} }>"
false => "{ \{s} }"
}
}
///|
pub fn StructType::full_info(self : Self) -> String {
match self.getName() {
Some(ident) =>
"%\{ident} = " +
(match self.isOpaque() {
true => "opaque"
false => "type " + self.body_str()
})
None => self.body_str()
}
}
///|
pub impl Show for StructType with output(self, logger : &Logger) {
let s = match self.getName() {
Some(name) => "%\{name}"
None => self.body_str()
}
logger.write_string(s)
}
///|
pub impl Eq for StructType with equal(self, other) {
match (self.name, other.name) {
(Some(n1), Some(n2)) => n1 == n2
(Some(_), None) => false
(None, Some(_)) => false
(None, None) => self.elements == other.elements
}
}
///|
pub impl Type for StructType with asTypeEnum(self) -> TypeEnum {
StructType(self)
}
///|
pub impl Type for StructType with getContext(self) -> Context {
self.ctx
}
///|
pub impl AggregateType for StructType with asAggregateTypeEnum(self) -> AggregateTypeEnum {
StructType(self)
}