use std::fmt;
use crate::ast::BinOp;
use crate::intern::InternedStr;
#[derive(Debug, Clone)]
pub enum TypeRepr {
CType {
specs: CTypeSpecs,
derived: Vec<CDerivedType>,
source: CTypeSource,
},
RustType {
repr: RustTypeRepr,
source: RustTypeSource,
},
Inferred(InferredType),
}
#[derive(Debug, Clone)]
pub enum CTypeSource {
Header,
Apidoc { raw: String },
InlineFn { func_name: InternedStr },
Parser,
FieldInference { field_name: InternedStr },
Cast,
SvFamilyCast,
CommonMacroFieldInference,
}
#[derive(Debug, Clone)]
pub enum RustTypeSource {
FnParam { func_name: String, param_index: usize },
FnReturn { func_name: String },
Const { const_name: String },
Parsed { raw: String },
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum CTypeSpecs {
Void,
Char { signed: Option<bool> },
Int { signed: bool, size: IntSize },
Float,
Double { is_long: bool },
Bool,
Struct { name: Option<InternedStr>, is_union: bool },
Enum { name: Option<InternedStr> },
TypedefName(InternedStr),
UnknownTypedef(String),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum IntSize {
Short,
Int,
Long,
LongLong,
Int128,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum CDerivedType {
Pointer {
is_const: bool,
is_volatile: bool,
is_restrict: bool,
},
Array { size: Option<usize> },
Function {
params: Vec<CTypeSpecs>,
variadic: bool,
},
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum RustTypeRepr {
CPrimitive(CPrimitiveKind),
RustPrimitive(RustPrimitiveKind),
Pointer {
inner: Box<RustTypeRepr>,
is_const: bool,
},
Reference {
inner: Box<RustTypeRepr>,
is_mut: bool,
},
Named(String),
Option(Box<RustTypeRepr>),
FnPointer {
params: Vec<RustTypeRepr>,
ret: Option<Box<RustTypeRepr>>,
},
Unit,
Unknown(String),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CPrimitiveKind {
CChar,
CSchar,
CUchar,
CShort,
CUshort,
CInt,
CUint,
CLong,
CUlong,
CLongLong,
CUlongLong,
CFloat,
CDouble,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RustPrimitiveKind {
I8,
I16,
I32,
I64,
I128,
Isize,
U8,
U16,
U32,
U64,
U128,
Usize,
F32,
F64,
Bool,
}
#[derive(Debug, Clone)]
pub enum InferredType {
IntLiteral,
UIntLiteral,
FloatLiteral,
CharLiteral,
StringLiteral,
SymbolLookup {
name: InternedStr,
resolved_type: Box<TypeRepr>,
},
ThxDefault,
BinaryOp {
op: BinOp,
result_type: Box<TypeRepr>,
},
UnaryArithmetic {
inner_type: Box<TypeRepr>,
},
LogicalNot,
AddressOf { inner_type: Box<TypeRepr> },
Dereference { pointer_type: Box<TypeRepr> },
IncDec { inner_type: Box<TypeRepr> },
MemberAccess {
base_type: String,
member: InternedStr,
field_type: Option<Box<TypeRepr>>,
},
PtrMemberAccess {
base_type: String,
member: InternedStr,
field_type: Option<Box<TypeRepr>>,
used_consistent_type: bool,
},
ArraySubscript {
base_type: Box<TypeRepr>,
element_type: Box<TypeRepr>,
},
Conditional {
then_type: Box<TypeRepr>,
else_type: Box<TypeRepr>,
result_type: Box<TypeRepr>,
},
Comma {
rhs_type: Box<TypeRepr>,
},
Assignment {
lhs_type: Box<TypeRepr>,
},
Cast { target_type: Box<TypeRepr> },
Sizeof,
Alignof,
CompoundLiteral { type_name: Box<TypeRepr> },
StmtExpr {
last_expr_type: Option<Box<TypeRepr>>,
},
Assert,
FunctionReturn { func_name: InternedStr },
}
impl CTypeSpecs {
pub fn from_decl_specs(specs: &crate::ast::DeclSpecs, _interner: &crate::intern::StringInterner) -> Self {
use crate::ast::TypeSpec;
let mut has_signed = false;
let mut has_unsigned = false;
let mut has_short = false;
let mut has_long: u8 = 0;
let mut base_type: Option<CTypeSpecs> = None;
for type_spec in &specs.type_specs {
match type_spec {
TypeSpec::Void => base_type = Some(CTypeSpecs::Void),
TypeSpec::Char => {
if base_type.is_none() {
base_type = Some(CTypeSpecs::Char { signed: None });
}
}
TypeSpec::Short => has_short = true,
TypeSpec::Int => {
if base_type.is_none() {
base_type = Some(CTypeSpecs::Int {
signed: true,
size: IntSize::Int,
});
}
}
TypeSpec::Long => has_long += 1,
TypeSpec::Float => base_type = Some(CTypeSpecs::Float),
TypeSpec::Double => base_type = Some(CTypeSpecs::Double { is_long: false }),
TypeSpec::Signed => has_signed = true,
TypeSpec::Unsigned => has_unsigned = true,
TypeSpec::Bool => base_type = Some(CTypeSpecs::Bool),
TypeSpec::Int128 => {
base_type = Some(CTypeSpecs::Int {
signed: !has_unsigned,
size: IntSize::Int128,
});
}
TypeSpec::Struct(s) => {
base_type = Some(CTypeSpecs::Struct {
name: s.name,
is_union: false,
});
}
TypeSpec::Union(s) => {
base_type = Some(CTypeSpecs::Struct {
name: s.name,
is_union: true,
});
}
TypeSpec::Enum(e) => {
base_type = Some(CTypeSpecs::Enum { name: e.name });
}
TypeSpec::TypedefName(name) => {
base_type = Some(CTypeSpecs::TypedefName(*name));
}
_ => {}
}
}
if has_short {
return CTypeSpecs::Int {
signed: !has_unsigned,
size: IntSize::Short,
};
}
if has_long >= 2 {
return CTypeSpecs::Int {
signed: !has_unsigned,
size: IntSize::LongLong,
};
}
if has_long == 1 {
if let Some(CTypeSpecs::Double { .. }) = base_type {
return CTypeSpecs::Double { is_long: true };
}
return CTypeSpecs::Int {
signed: !has_unsigned,
size: IntSize::Long,
};
}
if let Some(CTypeSpecs::Char { .. }) = base_type {
if has_signed {
return CTypeSpecs::Char { signed: Some(true) };
} else if has_unsigned {
return CTypeSpecs::Char { signed: Some(false) };
}
return CTypeSpecs::Char { signed: None };
}
if has_unsigned && base_type.is_none() {
return CTypeSpecs::Int {
signed: false,
size: IntSize::Int,
};
}
if has_signed && base_type.is_none() {
return CTypeSpecs::Int {
signed: true,
size: IntSize::Int,
};
}
if has_unsigned {
if let Some(CTypeSpecs::Int { size, .. }) = base_type {
return CTypeSpecs::Int {
signed: false,
size,
};
}
}
base_type.unwrap_or(CTypeSpecs::Int {
signed: true,
size: IntSize::Int,
})
}
}
impl CDerivedType {
pub fn from_derived_decls(derived: &[crate::ast::DerivedDecl]) -> Vec<Self> {
use crate::ast::ExprKind;
derived
.iter()
.map(|d| match d {
crate::ast::DerivedDecl::Pointer(quals) => CDerivedType::Pointer {
is_const: quals.is_const,
is_volatile: quals.is_volatile,
is_restrict: quals.is_restrict,
},
crate::ast::DerivedDecl::Array(array_decl) => {
let size = array_decl.size.as_ref().and_then(|expr| {
match &expr.kind {
ExprKind::IntLit(n) => Some(*n as usize),
ExprKind::UIntLit(n) => Some(*n as usize),
_ => None,
}
});
CDerivedType::Array { size }
}
crate::ast::DerivedDecl::Function(_params) => {
CDerivedType::Function {
params: vec![],
variadic: false,
}
}
})
.collect()
}
}
impl RustTypeRepr {
pub fn from_type_string(s: &str) -> Self {
let s = s.trim();
if s == "()" {
return RustTypeRepr::Unit;
}
if let Some(rest) = s.strip_prefix("*mut ") {
return RustTypeRepr::Pointer {
inner: Box::new(Self::from_type_string(rest)),
is_const: false,
};
}
if let Some(rest) = s.strip_prefix("* mut ") {
return RustTypeRepr::Pointer {
inner: Box::new(Self::from_type_string(rest)),
is_const: false,
};
}
if let Some(rest) = s.strip_prefix("*const ") {
return RustTypeRepr::Pointer {
inner: Box::new(Self::from_type_string(rest)),
is_const: true,
};
}
if let Some(rest) = s.strip_prefix("* const ") {
return RustTypeRepr::Pointer {
inner: Box::new(Self::from_type_string(rest)),
is_const: true,
};
}
if let Some(rest) = s.strip_prefix("&mut ") {
return RustTypeRepr::Reference {
inner: Box::new(Self::from_type_string(rest)),
is_mut: true,
};
}
if let Some(rest) = s.strip_prefix("& mut ") {
return RustTypeRepr::Reference {
inner: Box::new(Self::from_type_string(rest)),
is_mut: true,
};
}
if let Some(rest) = s.strip_prefix('&') {
return RustTypeRepr::Reference {
inner: Box::new(Self::from_type_string(rest.trim())),
is_mut: false,
};
}
if let Some(kind) = Self::parse_c_primitive(s) {
return RustTypeRepr::CPrimitive(kind);
}
if let Some(kind) = Self::parse_rust_primitive(s) {
return RustTypeRepr::RustPrimitive(kind);
}
if s.starts_with("Option<") || s.starts_with(":: std :: option :: Option<") {
if let Some(inner) = Self::extract_generic_param(s, "Option") {
return RustTypeRepr::Option(Box::new(Self::from_type_string(&inner)));
}
}
if s.chars().next().map(|c| c.is_alphabetic() || c == '_').unwrap_or(false) {
let name = s.split("::").last().unwrap_or(s).trim();
return RustTypeRepr::Named(name.to_string());
}
RustTypeRepr::Unknown(s.to_string())
}
fn parse_c_primitive(s: &str) -> Option<CPrimitiveKind> {
let s = s.trim();
let name = if s.contains("::") {
s.split("::").last()?.trim()
} else {
s
};
match name {
"c_char" => Some(CPrimitiveKind::CChar),
"c_schar" => Some(CPrimitiveKind::CSchar),
"c_uchar" => Some(CPrimitiveKind::CUchar),
"c_short" => Some(CPrimitiveKind::CShort),
"c_ushort" => Some(CPrimitiveKind::CUshort),
"c_int" => Some(CPrimitiveKind::CInt),
"c_uint" => Some(CPrimitiveKind::CUint),
"c_long" => Some(CPrimitiveKind::CLong),
"c_ulong" => Some(CPrimitiveKind::CUlong),
"c_longlong" => Some(CPrimitiveKind::CLongLong),
"c_ulonglong" => Some(CPrimitiveKind::CUlongLong),
"c_float" => Some(CPrimitiveKind::CFloat),
"c_double" => Some(CPrimitiveKind::CDouble),
_ => None,
}
}
fn parse_rust_primitive(s: &str) -> Option<RustPrimitiveKind> {
match s.trim() {
"i8" => Some(RustPrimitiveKind::I8),
"i16" => Some(RustPrimitiveKind::I16),
"i32" => Some(RustPrimitiveKind::I32),
"i64" => Some(RustPrimitiveKind::I64),
"i128" => Some(RustPrimitiveKind::I128),
"isize" => Some(RustPrimitiveKind::Isize),
"u8" => Some(RustPrimitiveKind::U8),
"u16" => Some(RustPrimitiveKind::U16),
"u32" => Some(RustPrimitiveKind::U32),
"u64" => Some(RustPrimitiveKind::U64),
"u128" => Some(RustPrimitiveKind::U128),
"usize" => Some(RustPrimitiveKind::Usize),
"f32" => Some(RustPrimitiveKind::F32),
"f64" => Some(RustPrimitiveKind::F64),
"bool" => Some(RustPrimitiveKind::Bool),
_ => None,
}
}
fn extract_generic_param(s: &str, type_name: &str) -> Option<String> {
let start = s.find(&format!("{}<", type_name))?;
let after_open = start + type_name.len() + 1;
let content = &s[after_open..];
let mut depth = 1;
let mut end = 0;
for (i, c) in content.char_indices() {
match c {
'<' => depth += 1,
'>' => {
depth -= 1;
if depth == 0 {
end = i;
break;
}
}
_ => {}
}
}
if end > 0 {
Some(content[..end].trim().to_string())
} else {
None
}
}
}
impl TypeRepr {
pub fn source_display(&self) -> &'static str {
match self {
TypeRepr::CType { source, .. } => match source {
CTypeSource::Header => "c-header",
CTypeSource::Apidoc { .. } => "apidoc",
CTypeSource::InlineFn { .. } => "inline-fn",
CTypeSource::Parser => "parser",
CTypeSource::FieldInference { .. } => "field-inference",
CTypeSource::Cast => "cast",
CTypeSource::SvFamilyCast => "sv-family-cast",
CTypeSource::CommonMacroFieldInference => "common-macro-field-inference",
},
TypeRepr::RustType { .. } => "rust-bindings",
TypeRepr::Inferred(_) => "inferred",
}
}
pub fn is_fn_param_source(&self) -> bool {
matches!(self, TypeRepr::RustType { source: RustTypeSource::FnParam { .. }, .. })
}
pub fn confidence_tier(&self) -> u8 {
match self {
TypeRepr::RustType { source, .. } => match source {
RustTypeSource::FnParam { .. }
| RustTypeSource::FnReturn { .. }
| RustTypeSource::Const { .. } => 1,
RustTypeSource::Parsed { .. } => 3,
},
TypeRepr::CType { source, .. } => match source {
CTypeSource::InlineFn { .. } | CTypeSource::Header => 2,
CTypeSource::Apidoc { .. }
| CTypeSource::CommonMacroFieldInference => 3,
CTypeSource::Cast
| CTypeSource::SvFamilyCast
| CTypeSource::FieldInference { .. }
| CTypeSource::Parser => 4,
},
TypeRepr::Inferred(_) => 4,
}
}
pub fn is_void(&self) -> bool {
match self {
TypeRepr::CType { specs, derived, .. } => {
derived.is_empty() && matches!(specs, CTypeSpecs::Void)
}
TypeRepr::RustType { repr, .. } => {
matches!(repr, RustTypeRepr::Unit)
}
TypeRepr::Inferred(inferred) => {
match inferred {
InferredType::SymbolLookup { resolved_type, .. } => {
resolved_type.is_void()
}
_ => false,
}
}
}
}
pub fn make_outer_pointer_mut(&mut self) {
match self {
TypeRepr::CType { derived, .. } => {
for d in derived.iter_mut().rev() {
if let CDerivedType::Pointer { is_const, .. } = d {
*is_const = false;
return;
}
}
}
TypeRepr::RustType { repr, .. } => {
if let RustTypeRepr::Pointer { is_const, .. } = repr {
*is_const = false;
}
}
_ => {}
}
}
pub fn make_outer_pointer_const(&mut self) {
match self {
TypeRepr::CType { derived, .. } => {
for d in derived.iter_mut().rev() {
if let CDerivedType::Pointer { is_const, .. } = d {
*is_const = true;
return;
}
}
}
TypeRepr::RustType { repr, .. } => {
repr.make_outer_pointer_const();
}
TypeRepr::Inferred(inferred) => {
match inferred {
InferredType::SymbolLookup { resolved_type, .. } => {
resolved_type.make_outer_pointer_const();
}
InferredType::Cast { target_type } => {
target_type.make_outer_pointer_const();
}
_ => {}
}
}
}
}
pub fn is_pointer_type(&self) -> bool {
match self {
TypeRepr::CType { derived, .. } => {
derived.iter().any(|d| matches!(d, CDerivedType::Pointer { .. }))
}
TypeRepr::RustType { repr, .. } => repr.has_outer_pointer(),
TypeRepr::Inferred(inferred) => inferred
.resolved_type()
.is_some_and(|t| t.is_pointer_type()),
}
}
pub fn is_void_pointer(&self) -> bool {
match self {
TypeRepr::CType { specs, derived, .. } => {
derived.iter().any(|d| matches!(d, CDerivedType::Pointer { .. }))
&& matches!(specs, CTypeSpecs::Void)
}
TypeRepr::RustType { repr, .. } => match repr {
RustTypeRepr::Pointer { inner, .. } => {
matches!(inner.as_ref(), RustTypeRepr::Unit)
|| matches!(inner.as_ref(), RustTypeRepr::Named(n) if n == "c_void")
}
_ => false,
},
TypeRepr::Inferred(inferred) => inferred
.resolved_type()
.is_some_and(|t| t.is_void_pointer()),
}
}
pub fn is_concrete_pointer(&self) -> bool {
self.is_pointer_type() && !self.is_void_pointer()
}
pub fn has_outer_pointer(&self) -> bool {
match self {
TypeRepr::CType { derived, .. } => {
derived.iter().any(|d| matches!(d, CDerivedType::Pointer { .. }))
}
TypeRepr::RustType { repr, .. } => repr.has_outer_pointer(),
_ => false,
}
}
pub fn from_apidoc_string(s: &str, interner: &crate::intern::StringInterner) -> Self {
let (specs, derived) = Self::parse_c_type_string(s, interner);
TypeRepr::CType {
specs,
derived,
source: CTypeSource::Apidoc { raw: s.to_string() },
}
}
pub fn from_rust_string(s: &str) -> Self {
let repr = RustTypeRepr::from_type_string(s);
TypeRepr::RustType {
repr,
source: RustTypeSource::Parsed {
raw: s.to_string(),
},
}
}
pub fn from_unified_type(
ut: &crate::unified_type::UnifiedType,
interner: &crate::intern::StringInterner,
) -> Self {
let raw = ut.to_rust_string();
if let Some((specs, derived)) = unified_to_c(ut, interner) {
return TypeRepr::CType {
specs,
derived,
source: CTypeSource::Apidoc { raw },
};
}
TypeRepr::RustType {
repr: RustTypeRepr::Unknown(raw.clone()),
source: RustTypeSource::Parsed { raw },
}
}
pub fn from_decl(
specs: &crate::ast::DeclSpecs,
declarator: &crate::ast::Declarator,
_interner: &crate::intern::StringInterner,
) -> Self {
let c_specs = CTypeSpecs::from_decl_specs(specs, _interner);
let derived = CDerivedType::from_derived_decls(&declarator.derived);
TypeRepr::CType {
specs: c_specs,
derived,
source: CTypeSource::Header,
}
}
pub fn from_type_name(
type_name: &crate::ast::TypeName,
interner: &crate::intern::StringInterner,
) -> Self {
let c_specs = CTypeSpecs::from_decl_specs(&type_name.specs, interner);
let derived = type_name.declarator
.as_ref()
.map(|d| CDerivedType::from_derived_decls(&d.derived))
.unwrap_or_default();
TypeRepr::CType {
specs: c_specs,
derived,
source: CTypeSource::Parser,
}
}
pub fn from_c_type_string(
s: &str,
interner: &crate::intern::StringInterner,
files: &crate::source::FileRegistry,
typedefs: &std::collections::HashSet<crate::intern::InternedStr>,
) -> Self {
use crate::parser::parse_type_from_string;
match parse_type_from_string(s, interner, files, typedefs) {
Ok(type_name) => Self::from_type_name(&type_name, interner),
Err(_) => {
let (specs, derived) = Self::parse_c_type_string(s, interner);
TypeRepr::CType {
specs,
derived,
source: CTypeSource::Apidoc { raw: s.to_string() },
}
}
}
}
fn parse_c_type_string(s: &str, interner: &crate::intern::StringInterner) -> (CTypeSpecs, Vec<CDerivedType>) {
let s = s.trim();
let mut prefix_pointers: Vec<bool> = Vec::new(); let mut current = s;
loop {
if let Some(rest) = current.strip_prefix("*mut ") {
prefix_pointers.push(false);
current = rest.trim();
} else if let Some(rest) = current.strip_prefix("*const ") {
prefix_pointers.push(true);
current = rest.trim();
} else {
break;
}
}
let mut ptr_count = 0;
let mut is_const = false;
let mut base = current;
while base.ends_with('*') {
ptr_count += 1;
base = base[..base.len() - 1].trim();
}
if base.starts_with("const ") {
is_const = true;
base = base[6..].trim();
}
if base.ends_with(" const") {
is_const = true;
base = base[..base.len() - 6].trim();
}
let specs = Self::parse_c_base_type(base, interner);
let mut derived: Vec<CDerivedType> = Vec::with_capacity(prefix_pointers.len() + ptr_count);
for is_const_p in prefix_pointers.iter().rev() {
derived.push(CDerivedType::Pointer {
is_const: *is_const_p,
is_volatile: false,
is_restrict: false,
});
}
for i in 0..ptr_count {
derived.push(CDerivedType::Pointer {
is_const: i == 0 && is_const,
is_volatile: false,
is_restrict: false,
});
}
(specs, derived)
}
fn parse_c_base_type(s: &str, interner: &crate::intern::StringInterner) -> CTypeSpecs {
match s {
"void" => CTypeSpecs::Void,
"char" => CTypeSpecs::Char { signed: None },
"signed char" => CTypeSpecs::Char { signed: Some(true) },
"unsigned char" => CTypeSpecs::Char { signed: Some(false) },
"short" | "short int" | "signed short" | "signed short int" => {
CTypeSpecs::Int { signed: true, size: IntSize::Short }
}
"unsigned short" | "unsigned short int" => {
CTypeSpecs::Int { signed: false, size: IntSize::Short }
}
"int" | "signed" | "signed int" => {
CTypeSpecs::Int { signed: true, size: IntSize::Int }
}
"unsigned" | "unsigned int" => {
CTypeSpecs::Int { signed: false, size: IntSize::Int }
}
"long" | "long int" | "signed long" | "signed long int" => {
CTypeSpecs::Int { signed: true, size: IntSize::Long }
}
"unsigned long" | "unsigned long int" => {
CTypeSpecs::Int { signed: false, size: IntSize::Long }
}
"long long" | "long long int" | "signed long long" | "signed long long int" => {
CTypeSpecs::Int { signed: true, size: IntSize::LongLong }
}
"unsigned long long" | "unsigned long long int" => {
CTypeSpecs::Int { signed: false, size: IntSize::LongLong }
}
"float" => CTypeSpecs::Float,
"double" => CTypeSpecs::Double { is_long: false },
"long double" => CTypeSpecs::Double { is_long: true },
"_Bool" | "bool" => CTypeSpecs::Bool,
_ => {
if let Some(rest) = s.strip_prefix("struct ") {
if let Some(name) = interner.lookup(rest.trim()) {
return CTypeSpecs::Struct { name: Some(name), is_union: false };
}
return CTypeSpecs::Struct { name: None, is_union: false };
}
if let Some(rest) = s.strip_prefix("union ") {
if let Some(name) = interner.lookup(rest.trim()) {
return CTypeSpecs::Struct { name: Some(name), is_union: true };
}
return CTypeSpecs::Struct { name: None, is_union: true };
}
if let Some(rest) = s.strip_prefix("enum ") {
if let Some(name) = interner.lookup(rest.trim()) {
return CTypeSpecs::Enum { name: Some(name) };
}
return CTypeSpecs::Enum { name: None };
}
if let Some(name) = interner.lookup(s) {
CTypeSpecs::TypedefName(name)
} else {
CTypeSpecs::Void }
}
}
}
pub fn to_display_string(&self, interner: &crate::intern::StringInterner) -> String {
match self {
TypeRepr::CType { specs, derived, .. } => {
let base = specs.to_display_string(interner);
let mut result = base;
for d in derived {
match d {
CDerivedType::Pointer { is_const: true, .. } => result.push_str(" *const"),
CDerivedType::Pointer { .. } => result.push_str(" *"),
CDerivedType::Array { size: Some(n) } => {
result.push_str(&format!("[{}]", n));
}
CDerivedType::Array { size: None } => result.push_str("[]"),
CDerivedType::Function { .. } => result.push_str("()"),
}
}
result
}
TypeRepr::RustType { repr, .. } => repr.to_display_string(),
TypeRepr::Inferred(inferred) => inferred.to_display_string(interner),
}
}
pub fn to_rust_string(&self, interner: &crate::intern::StringInterner) -> String {
match self {
TypeRepr::CType { specs, derived, .. } => {
let base = specs.to_rust_string(interner);
let mut result = base;
for d in derived.iter().rev() {
if result == "()" && matches!(d, CDerivedType::Pointer { .. } | CDerivedType::Array { .. }) {
result = "c_void".to_string();
}
result = match d {
CDerivedType::Pointer { is_const: true, .. } => format!("*const {}", result),
CDerivedType::Pointer { .. } => format!("*mut {}", result),
CDerivedType::Array { size: Some(n) } => format!("[{}; {}]", result, n),
CDerivedType::Array { size: None } => format!("*mut {}", result),
CDerivedType::Function { .. } => format!("/* fn */"),
};
}
result
}
TypeRepr::RustType { repr, .. } => repr.to_display_string(),
TypeRepr::Inferred(inferred) => inferred.to_rust_string(interner),
}
}
}
fn unified_to_c(
ut: &crate::unified_type::UnifiedType,
interner: &crate::intern::StringInterner,
) -> Option<(CTypeSpecs, Vec<CDerivedType>)> {
use crate::unified_type::{UnifiedType as UT, IntSize as UIS};
match ut {
UT::Void => Some((CTypeSpecs::Void, vec![])),
UT::Bool => Some((CTypeSpecs::Bool, vec![])),
UT::Char { signed } => Some((CTypeSpecs::Char { signed: *signed }, vec![])),
UT::Int { signed, size } => {
if matches!(size, UIS::Char) {
return Some((CTypeSpecs::Char { signed: Some(*signed) }, vec![]));
}
let target = match size {
UIS::Char => unreachable!(),
UIS::Short => IntSize::Short,
UIS::Int => IntSize::Int,
UIS::Long => IntSize::Long,
UIS::LongLong => IntSize::LongLong,
UIS::Int128 => IntSize::Int128,
};
Some((CTypeSpecs::Int { signed: *signed, size: target }, vec![]))
}
UT::Float => Some((CTypeSpecs::Float, vec![])),
UT::Double => Some((CTypeSpecs::Double { is_long: false }, vec![])),
UT::LongDouble => Some((CTypeSpecs::Double { is_long: true }, vec![])),
UT::Pointer { inner, is_const } => {
let (specs, mut derived) = unified_to_c(inner, interner)?;
derived.insert(
0,
CDerivedType::Pointer {
is_const: *is_const,
is_volatile: false,
is_restrict: false,
},
);
Some((specs, derived))
}
UT::Array { inner, size } => {
let (specs, mut derived) = unified_to_c(inner, interner)?;
derived.insert(0, CDerivedType::Array { size: *size });
Some((specs, derived))
}
UT::Named(name) => {
let specs = match interner.lookup(name) {
Some(id) => CTypeSpecs::TypedefName(id),
None => CTypeSpecs::UnknownTypedef(name.clone()),
};
Some((specs, vec![]))
}
UT::FnPtr { .. } | UT::Verbatim(_) | UT::Unknown => None,
}
}
impl TypeRepr {
pub fn pointee_name(&self) -> Option<InternedStr> {
match self {
TypeRepr::CType { specs, derived, .. } => {
if derived.iter().any(|d| matches!(d, CDerivedType::Pointer { .. })) {
specs.type_name()
} else {
None
}
}
TypeRepr::RustType { repr, .. } => repr.pointee_name(),
TypeRepr::Inferred(inferred) => inferred.resolved_type()?.pointee_name(),
}
}
pub fn type_name(&self) -> Option<InternedStr> {
match self {
TypeRepr::CType { specs, .. } => specs.type_name(),
TypeRepr::RustType { repr, .. } => repr.type_name(),
TypeRepr::Inferred(inferred) => inferred.resolved_type()?.type_name(),
}
}
}
impl CTypeSpecs {
pub fn type_name(&self) -> Option<InternedStr> {
match self {
CTypeSpecs::Struct { name: Some(n), .. } => Some(*n),
CTypeSpecs::TypedefName(n) => Some(*n),
CTypeSpecs::Enum { name: Some(n) } => Some(*n),
_ => None,
}
}
}
impl InferredType {
pub fn resolved_type(&self) -> Option<&TypeRepr> {
match self {
InferredType::Cast { target_type } => Some(target_type),
InferredType::PtrMemberAccess { field_type: Some(ft), .. } => Some(ft),
InferredType::MemberAccess { field_type: Some(ft), .. } => Some(ft),
InferredType::ArraySubscript { element_type, .. } => Some(element_type),
InferredType::AddressOf { inner_type } => Some(inner_type),
InferredType::Dereference { pointer_type } => Some(pointer_type),
InferredType::SymbolLookup { resolved_type, .. } => Some(resolved_type),
InferredType::IncDec { inner_type } => Some(inner_type),
InferredType::Assignment { lhs_type } => Some(lhs_type),
InferredType::Comma { rhs_type } => Some(rhs_type),
InferredType::Conditional { result_type, .. } => Some(result_type),
InferredType::BinaryOp { result_type, .. } => Some(result_type),
InferredType::UnaryArithmetic { inner_type } => Some(inner_type),
InferredType::CompoundLiteral { type_name } => Some(type_name),
InferredType::StmtExpr { last_expr_type } => last_expr_type.as_deref(),
_ => None,
}
}
}
impl RustTypeRepr {
fn pointee_name(&self) -> Option<InternedStr> {
None
}
fn type_name(&self) -> Option<InternedStr> {
None
}
}
impl CTypeSpecs {
pub fn to_display_string(&self, interner: &crate::intern::StringInterner) -> String {
match self {
CTypeSpecs::Void => "void".to_string(),
CTypeSpecs::Char { signed: None } => "char".to_string(),
CTypeSpecs::Char { signed: Some(true) } => "signed char".to_string(),
CTypeSpecs::Char { signed: Some(false) } => "unsigned char".to_string(),
CTypeSpecs::Int { signed: true, size: IntSize::Short } => "short".to_string(),
CTypeSpecs::Int { signed: false, size: IntSize::Short } => "unsigned short".to_string(),
CTypeSpecs::Int { signed: true, size: IntSize::Int } => "int".to_string(),
CTypeSpecs::Int { signed: false, size: IntSize::Int } => "unsigned int".to_string(),
CTypeSpecs::Int { signed: true, size: IntSize::Long } => "long".to_string(),
CTypeSpecs::Int { signed: false, size: IntSize::Long } => "unsigned long".to_string(),
CTypeSpecs::Int { signed: true, size: IntSize::LongLong } => "long long".to_string(),
CTypeSpecs::Int { signed: false, size: IntSize::LongLong } => "unsigned long long".to_string(),
CTypeSpecs::Int { signed: true, size: IntSize::Int128 } => "__int128".to_string(),
CTypeSpecs::Int { signed: false, size: IntSize::Int128 } => "unsigned __int128".to_string(),
CTypeSpecs::Float => "float".to_string(),
CTypeSpecs::Double { is_long: false } => "double".to_string(),
CTypeSpecs::Double { is_long: true } => "long double".to_string(),
CTypeSpecs::Bool => "_Bool".to_string(),
CTypeSpecs::Struct { name: Some(n), is_union: false } => {
format!("struct {}", interner.get(*n))
}
CTypeSpecs::Struct { name: None, is_union: false } => "struct".to_string(),
CTypeSpecs::Struct { name: Some(n), is_union: true } => {
format!("union {}", interner.get(*n))
}
CTypeSpecs::Struct { name: None, is_union: true } => "union".to_string(),
CTypeSpecs::Enum { name: Some(n) } => format!("enum {}", interner.get(*n)),
CTypeSpecs::Enum { name: None } => "enum".to_string(),
CTypeSpecs::TypedefName(n) => interner.get(*n).to_string(),
CTypeSpecs::UnknownTypedef(s) => s.clone(),
}
}
pub fn to_rust_string(&self, interner: &crate::intern::StringInterner) -> String {
match self {
CTypeSpecs::Void => "()".to_string(),
CTypeSpecs::Char { signed: None } => "c_char".to_string(),
CTypeSpecs::Char { signed: Some(true) } => "c_schar".to_string(),
CTypeSpecs::Char { signed: Some(false) } => "c_uchar".to_string(),
CTypeSpecs::Int { signed: true, size: IntSize::Short } => "c_short".to_string(),
CTypeSpecs::Int { signed: false, size: IntSize::Short } => "c_ushort".to_string(),
CTypeSpecs::Int { signed: true, size: IntSize::Int } => "c_int".to_string(),
CTypeSpecs::Int { signed: false, size: IntSize::Int } => "c_uint".to_string(),
CTypeSpecs::Int { signed: true, size: IntSize::Long } => "c_long".to_string(),
CTypeSpecs::Int { signed: false, size: IntSize::Long } => "c_ulong".to_string(),
CTypeSpecs::Int { signed: true, size: IntSize::LongLong } => "c_longlong".to_string(),
CTypeSpecs::Int { signed: false, size: IntSize::LongLong } => "c_ulonglong".to_string(),
CTypeSpecs::Int { signed: true, size: IntSize::Int128 } => "i128".to_string(),
CTypeSpecs::Int { signed: false, size: IntSize::Int128 } => "u128".to_string(),
CTypeSpecs::Float => "c_float".to_string(),
CTypeSpecs::Double { is_long: false } => "c_double".to_string(),
CTypeSpecs::Double { is_long: true } => "c_double".to_string(), CTypeSpecs::Bool => "bool".to_string(),
CTypeSpecs::Struct { name: Some(n), .. } => interner.get(*n).to_string(),
CTypeSpecs::Struct { name: None, .. } => "/* anonymous struct */".to_string(),
CTypeSpecs::Enum { name: Some(n) } => interner.get(*n).to_string(),
CTypeSpecs::Enum { name: None } => "/* anonymous enum */".to_string(),
CTypeSpecs::TypedefName(n) => interner.get(*n).to_string(),
CTypeSpecs::UnknownTypedef(s) => s.clone(),
}
}
}
impl RustTypeRepr {
pub fn make_outer_pointer_const(&mut self) {
if let RustTypeRepr::Pointer { is_const, .. } = self {
*is_const = true;
}
}
pub fn has_outer_pointer(&self) -> bool {
matches!(self, RustTypeRepr::Pointer { .. })
}
}
impl RustTypeRepr {
pub fn to_display_string(&self) -> String {
match self {
RustTypeRepr::CPrimitive(kind) => kind.to_string(),
RustTypeRepr::RustPrimitive(kind) => kind.to_string(),
RustTypeRepr::Pointer { inner, is_const: true } => {
format!("*const {}", inner.to_display_string())
}
RustTypeRepr::Pointer { inner, is_const: false } => {
format!("*mut {}", inner.to_display_string())
}
RustTypeRepr::Reference { inner, is_mut: true } => {
format!("&mut {}", inner.to_display_string())
}
RustTypeRepr::Reference { inner, is_mut: false } => {
format!("&{}", inner.to_display_string())
}
RustTypeRepr::Named(name) => name.clone(),
RustTypeRepr::Option(inner) => format!("Option<{}>", inner.to_display_string()),
RustTypeRepr::FnPointer { params, ret } => {
let params_str: Vec<_> = params.iter().map(|p| p.to_display_string()).collect();
let ret_str = ret
.as_ref()
.map(|r| format!(" -> {}", r.to_display_string()))
.unwrap_or_default();
format!("fn({}){}", params_str.join(", "), ret_str)
}
RustTypeRepr::Unit => "()".to_string(),
RustTypeRepr::Unknown(s) => s.clone(),
}
}
}
impl fmt::Display for CPrimitiveKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let s = match self {
CPrimitiveKind::CChar => "c_char",
CPrimitiveKind::CSchar => "c_schar",
CPrimitiveKind::CUchar => "c_uchar",
CPrimitiveKind::CShort => "c_short",
CPrimitiveKind::CUshort => "c_ushort",
CPrimitiveKind::CInt => "c_int",
CPrimitiveKind::CUint => "c_uint",
CPrimitiveKind::CLong => "c_long",
CPrimitiveKind::CUlong => "c_ulong",
CPrimitiveKind::CLongLong => "c_longlong",
CPrimitiveKind::CUlongLong => "c_ulonglong",
CPrimitiveKind::CFloat => "c_float",
CPrimitiveKind::CDouble => "c_double",
};
write!(f, "{}", s)
}
}
impl fmt::Display for RustPrimitiveKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let s = match self {
RustPrimitiveKind::I8 => "i8",
RustPrimitiveKind::I16 => "i16",
RustPrimitiveKind::I32 => "i32",
RustPrimitiveKind::I64 => "i64",
RustPrimitiveKind::I128 => "i128",
RustPrimitiveKind::Isize => "isize",
RustPrimitiveKind::U8 => "u8",
RustPrimitiveKind::U16 => "u16",
RustPrimitiveKind::U32 => "u32",
RustPrimitiveKind::U64 => "u64",
RustPrimitiveKind::U128 => "u128",
RustPrimitiveKind::Usize => "usize",
RustPrimitiveKind::F32 => "f32",
RustPrimitiveKind::F64 => "f64",
RustPrimitiveKind::Bool => "bool",
};
write!(f, "{}", s)
}
}
impl InferredType {
pub fn to_display_string(&self, interner: &crate::intern::StringInterner) -> String {
match self {
InferredType::IntLiteral => "int".to_string(),
InferredType::UIntLiteral => "unsigned int".to_string(),
InferredType::FloatLiteral => "double".to_string(),
InferredType::CharLiteral => "int".to_string(),
InferredType::StringLiteral => "char *".to_string(),
InferredType::SymbolLookup { resolved_type, .. } => {
resolved_type.to_display_string(interner)
}
InferredType::ThxDefault => "*mut PerlInterpreter".to_string(),
InferredType::BinaryOp { result_type, .. } => result_type.to_display_string(interner),
InferredType::UnaryArithmetic { inner_type } => inner_type.to_display_string(interner),
InferredType::LogicalNot => "int".to_string(),
InferredType::AddressOf { inner_type } => {
format!("{} *", inner_type.to_display_string(interner))
}
InferredType::Dereference { pointer_type } => {
let s = pointer_type.to_display_string(interner);
s.trim_end_matches(" *").to_string()
}
InferredType::IncDec { inner_type } => inner_type.to_display_string(interner),
InferredType::MemberAccess { field_type: Some(ft), .. } => {
ft.to_display_string(interner)
}
InferredType::MemberAccess { base_type, member, .. } => {
format!("{}.{}", base_type, interner.get(*member))
}
InferredType::PtrMemberAccess { field_type: Some(ft), .. } => {
ft.to_display_string(interner)
}
InferredType::PtrMemberAccess { base_type, member, .. } => {
format!("{}->{}", base_type, interner.get(*member))
}
InferredType::ArraySubscript { element_type, .. } => {
element_type.to_display_string(interner)
}
InferredType::Conditional { result_type, .. } => {
result_type.to_display_string(interner)
}
InferredType::Comma { rhs_type } => rhs_type.to_display_string(interner),
InferredType::Assignment { lhs_type } => lhs_type.to_display_string(interner),
InferredType::Cast { target_type } => target_type.to_display_string(interner),
InferredType::Sizeof | InferredType::Alignof => "unsigned long".to_string(),
InferredType::CompoundLiteral { type_name } => type_name.to_display_string(interner),
InferredType::StmtExpr { last_expr_type: Some(t) } => t.to_display_string(interner),
InferredType::StmtExpr { last_expr_type: None } => "void".to_string(),
InferredType::Assert => "void".to_string(),
InferredType::FunctionReturn { func_name } => {
format!("{}()", interner.get(*func_name))
}
}
}
pub fn to_rust_string(&self, interner: &crate::intern::StringInterner) -> String {
match self {
InferredType::IntLiteral => "c_int".to_string(),
InferredType::UIntLiteral => "c_uint".to_string(),
InferredType::FloatLiteral => "c_double".to_string(),
InferredType::CharLiteral => "c_int".to_string(),
InferredType::StringLiteral => "*const c_char".to_string(),
InferredType::SymbolLookup { resolved_type, .. } => {
resolved_type.to_rust_string(interner)
}
InferredType::ThxDefault => "*mut PerlInterpreter".to_string(),
InferredType::BinaryOp { result_type, .. } => result_type.to_rust_string(interner),
InferredType::UnaryArithmetic { inner_type } => inner_type.to_rust_string(interner),
InferredType::LogicalNot => "c_int".to_string(),
InferredType::AddressOf { inner_type } => {
format!("*mut {}", inner_type.to_rust_string(interner))
}
InferredType::Dereference { pointer_type } => {
let s = pointer_type.to_rust_string(interner);
s.strip_prefix("*mut ").or_else(|| s.strip_prefix("*const "))
.unwrap_or(&s).to_string()
}
InferredType::IncDec { inner_type } => inner_type.to_rust_string(interner),
InferredType::MemberAccess { field_type: Some(ft), .. } => {
ft.to_rust_string(interner)
}
InferredType::MemberAccess { base_type, member, .. } => {
format!("/* {}.{} */", base_type, interner.get(*member))
}
InferredType::PtrMemberAccess { field_type: Some(ft), .. } => {
ft.to_rust_string(interner)
}
InferredType::PtrMemberAccess { base_type, member, .. } => {
format!("/* {}->{} */", base_type, interner.get(*member))
}
InferredType::ArraySubscript { element_type, .. } => {
element_type.to_rust_string(interner)
}
InferredType::Conditional { result_type, .. } => {
result_type.to_rust_string(interner)
}
InferredType::Comma { rhs_type } => rhs_type.to_rust_string(interner),
InferredType::Assignment { lhs_type } => lhs_type.to_rust_string(interner),
InferredType::Cast { target_type } => target_type.to_rust_string(interner),
InferredType::Sizeof | InferredType::Alignof => "c_ulong".to_string(),
InferredType::CompoundLiteral { type_name } => type_name.to_rust_string(interner),
InferredType::StmtExpr { last_expr_type: Some(t) } => t.to_rust_string(interner),
InferredType::StmtExpr { last_expr_type: None } => "()".to_string(),
InferredType::Assert => "()".to_string(),
InferredType::FunctionReturn { func_name } => {
format!("/* {}() ret */", interner.get(*func_name))
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_rust_type_repr_from_string() {
assert!(matches!(
RustTypeRepr::from_type_string("c_int"),
RustTypeRepr::CPrimitive(CPrimitiveKind::CInt)
));
assert!(matches!(
RustTypeRepr::from_type_string("i32"),
RustTypeRepr::RustPrimitive(RustPrimitiveKind::I32)
));
assert!(matches!(
RustTypeRepr::from_type_string("()"),
RustTypeRepr::Unit
));
if let RustTypeRepr::Pointer { inner, is_const: false } =
RustTypeRepr::from_type_string("*mut SV")
{
assert!(matches!(*inner, RustTypeRepr::Named(ref n) if n == "SV"));
} else {
panic!("Expected *mut SV");
}
if let RustTypeRepr::Pointer { inner, is_const: true } =
RustTypeRepr::from_type_string("*const c_char")
{
assert!(matches!(*inner, RustTypeRepr::CPrimitive(CPrimitiveKind::CChar)));
} else {
panic!("Expected *const c_char");
}
}
#[test]
fn test_rust_type_repr_from_string_with_spaces() {
if let RustTypeRepr::Pointer { inner, is_const: false } =
RustTypeRepr::from_type_string("* mut SV")
{
assert!(matches!(*inner, RustTypeRepr::Named(ref n) if n == "SV"));
} else {
panic!("Expected * mut SV");
}
}
#[test]
fn test_c_primitive_display() {
assert_eq!(CPrimitiveKind::CInt.to_string(), "c_int");
assert_eq!(CPrimitiveKind::CUlong.to_string(), "c_ulong");
}
#[test]
fn test_rust_primitive_display() {
assert_eq!(RustPrimitiveKind::I32.to_string(), "i32");
assert_eq!(RustPrimitiveKind::Usize.to_string(), "usize");
}
fn make_void_ptr() -> TypeRepr {
TypeRepr::CType {
specs: CTypeSpecs::Void,
derived: vec![CDerivedType::Pointer {
is_const: false,
is_volatile: false,
is_restrict: false,
}],
source: CTypeSource::Apidoc { raw: "void *".to_string() },
}
}
fn make_concrete_ptr() -> TypeRepr {
TypeRepr::CType {
specs: CTypeSpecs::Char { signed: None },
derived: vec![CDerivedType::Pointer {
is_const: false,
is_volatile: false,
is_restrict: false,
}],
source: CTypeSource::Apidoc { raw: "char *".to_string() },
}
}
#[test]
fn test_void_pointer_structural() {
let vp = make_void_ptr();
assert!(vp.is_pointer_type());
assert!(vp.is_void_pointer());
assert!(!vp.is_concrete_pointer());
}
#[test]
fn test_concrete_pointer_structural() {
let cp = make_concrete_ptr();
assert!(cp.is_pointer_type());
assert!(!cp.is_void_pointer());
assert!(cp.is_concrete_pointer());
}
#[test]
fn test_inferred_member_access_pointer_recursive() {
let inner = make_concrete_ptr();
let inferred = TypeRepr::Inferred(InferredType::MemberAccess {
base_type: "xpvcv".to_string(),
member: crate::intern::StringInterner::new().intern("foo"),
field_type: Some(Box::new(inner)),
});
assert!(inferred.is_pointer_type());
assert!(!inferred.is_void_pointer());
assert!(inferred.is_concrete_pointer());
assert!(!inferred.has_outer_pointer());
}
}