use crate::kind::{ArrayLen, Qualifiers, TypeKind};
use crate::types::{TypeId, Types};
pub(crate) fn bare(types: &Types, id: TypeId) -> TypeKind {
match types.kind(types.canonical(id)) {
TypeKind::Atomic(inner) => types.kind(types.canonical(inner)),
other => other,
}
}
#[must_use]
pub fn is_void(types: &Types, id: TypeId) -> bool {
matches!(bare(types, id), TypeKind::Void)
}
#[must_use]
pub fn is_integer(types: &Types, id: TypeId) -> bool {
matches!(
bare(types, id),
TypeKind::Bool | TypeKind::Int(_) | TypeKind::BitInt { .. } | TypeKind::Enum(_)
)
}
#[must_use]
pub fn is_real_floating(types: &Types, id: TypeId) -> bool {
matches!(bare(types, id), TypeKind::Float(_))
}
#[must_use]
pub fn is_complex(types: &Types, id: TypeId) -> bool {
matches!(bare(types, id), TypeKind::Complex(_))
}
#[must_use]
pub fn is_floating(types: &Types, id: TypeId) -> bool {
matches!(bare(types, id), TypeKind::Float(_) | TypeKind::Complex(_))
}
#[must_use]
pub fn is_arithmetic(types: &Types, id: TypeId) -> bool {
is_integer(types, id) || is_floating(types, id)
}
#[must_use]
pub fn is_real(types: &Types, id: TypeId) -> bool {
is_integer(types, id) || is_real_floating(types, id)
}
#[must_use]
pub fn is_pointer(types: &Types, id: TypeId) -> bool {
matches!(bare(types, id), TypeKind::Pointer(_))
}
#[must_use]
pub fn pointee(types: &Types, id: TypeId) -> Option<TypeId> {
match bare(types, id) {
TypeKind::Pointer(inner) => Some(inner),
_ => None,
}
}
#[must_use]
pub fn is_array(types: &Types, id: TypeId) -> bool {
matches!(bare(types, id), TypeKind::Array { .. })
}
#[must_use]
pub fn element(types: &Types, id: TypeId) -> Option<TypeId> {
match bare(types, id) {
TypeKind::Array { elem, .. } | TypeKind::Vector { elem, .. } => Some(elem),
_ => None,
}
}
#[must_use]
pub fn is_function(types: &Types, id: TypeId) -> bool {
matches!(bare(types, id), TypeKind::Function(_))
}
#[must_use]
pub fn is_record(types: &Types, id: TypeId) -> bool {
matches!(bare(types, id), TypeKind::Record(_))
}
#[must_use]
pub fn is_vector(types: &Types, id: TypeId) -> bool {
matches!(bare(types, id), TypeKind::Vector { .. })
}
#[must_use]
pub fn is_atomic(types: &Types, id: TypeId) -> bool {
matches!(types.kind(types.canonical(id)), TypeKind::Atomic(_))
}
#[must_use]
pub fn is_scalar(types: &Types, id: TypeId) -> bool {
is_arithmetic(types, id) || is_pointer(types, id)
}
#[must_use]
pub fn is_aggregate(types: &Types, id: TypeId) -> bool {
match bare(types, id) {
TypeKind::Array { .. } => true,
TypeKind::Record(record) => {
matches!(types.record_info(record).kind, crate::kind::RecordKind::Struct)
}
_ => false,
}
}
#[must_use]
pub fn is_object(types: &Types, id: TypeId) -> bool {
!is_function(types, id)
}
#[must_use]
pub fn is_complete(types: &Types, id: TypeId) -> bool {
match bare(types, id) {
TypeKind::Void => false,
TypeKind::Array { len: ArrayLen::Unknown, .. } => false,
TypeKind::Array { elem, .. } => is_complete(types, elem),
TypeKind::Record(record) => types.record_info(record).layout.is_some(),
TypeKind::Enum(id) => types.enum_info(id).underlying.is_some(),
_ => true,
}
}
#[must_use]
pub fn is_modifiable(types: &Types, id: TypeId) -> bool {
if types.quals(id).has(Qualifiers::CONST) || is_array(types, id) || !is_complete(types, id) {
return false;
}
match bare(types, id) {
TypeKind::Record(record) => {
types.record_info(record).fields.iter().all(|field| is_modifiable(types, field.ty))
}
_ => true,
}
}
#[cfg(test)]
mod tests {
use rucc_base::Interner;
use rucc_target::{TargetInfo, Triple};
use super::*;
use crate::kind::{ArrayLen, FloatKind, IntKind, RecordKind};
use crate::record::{FieldDecl, RecordOptions, layout_record};
#[test]
fn an_enumeration_is_an_integer_type() {
let mut types = Types::new();
let id = types.declare_enum(None);
let int = types.int(IntKind::Int);
types.complete_enum(id, int, false);
let enumeration = types.enumeration(id);
assert!(is_integer(&types, enumeration));
assert!(is_arithmetic(&types, enumeration));
assert!(is_scalar(&types, enumeration));
}
#[test]
fn atomic_is_in_whatever_category_it_wraps() {
let mut types = Types::new();
let int = types.int(IntKind::Int);
let atomic = types.atomic(int);
assert!(is_integer(&types, atomic));
assert!(is_scalar(&types, atomic));
assert!(is_atomic(&types, atomic));
assert!(!is_atomic(&types, int));
}
#[test]
fn a_typedef_answers_as_what_it_names() {
let mut types = Types::new();
let mut names = Interner::new();
let int = types.int(IntKind::Int);
let name = names.intern("size_t");
let alias = types.typedef(name, int);
assert!(is_integer(&types, alias));
assert!(types.is_sugar(alias));
}
#[test]
fn a_complex_type_is_arithmetic_and_is_not_real() {
let mut types = Types::new();
let complex = types.complex(FloatKind::Double);
assert!(is_arithmetic(&types, complex));
assert!(is_floating(&types, complex));
assert!(!is_real(&types, complex));
}
#[test]
fn void_is_an_object_type_and_is_never_complete() {
let types = Types::new();
let void = types.void();
assert!(is_object(&types, void));
assert!(!is_complete(&types, void));
assert!(!is_scalar(&types, void));
}
#[test]
fn a_union_is_not_an_aggregate() {
let mut types = Types::new();
let union = types.declare_record(RecordKind::Union, None);
let union = types.record(union);
let int = types.int(IntKind::Int);
let array = types.array(int, ArrayLen::Fixed(2));
assert!(!is_aggregate(&types, union));
assert!(is_aggregate(&types, array));
}
#[test]
fn an_incomplete_record_is_an_object_type_that_cannot_be_made() {
let mut types = Types::new();
let record = types.declare_record(RecordKind::Struct, None);
let id = types.record(record);
assert!(is_object(&types, id));
assert!(!is_complete(&types, id));
assert!(!is_modifiable(&types, id));
}
#[test]
fn a_const_member_makes_the_whole_structure_unmodifiable() {
let mut types = Types::new();
let int = types.int(IntKind::Int);
let constant = types.qualified(int, Qualifiers::CONST);
let record = types.declare_record(RecordKind::Struct, None);
let target =
TargetInfo::new("x86_64-unknown-linux-gnu".parse::<Triple>().expect("a triple"));
let laid_out = layout_record(
&types,
RecordKind::Struct,
&[FieldDecl::new(None, constant)],
&RecordOptions::default(),
&target,
)
.expect("a layout");
types.complete_record(record, laid_out);
let id = types.record(record);
assert!(!is_modifiable(&types, id));
}
}