use std::collections::{HashMap, HashSet};
use crate::apidoc::ApidocDict;
use crate::ast::*;
use crate::fields_dict::FieldsDict;
use crate::inline_fn::InlineFnDict;
use crate::intern::{InternedStr, StringInterner};
use crate::parser::parse_type_from_string;
use crate::rust_decl::RustDeclDict;
use crate::source::{FileRegistry, SourceLocation};
use crate::type_env::{TypeEnv, TypeConstraint as TypeEnvConstraint};
use crate::type_repr::{
CTypeSource, CTypeSpecs, CDerivedType, InferredType,
RustTypeRepr, RustTypeSource, TypeRepr,
};
use crate::unified_type::{IntSize, UnifiedType};
fn leftmost_param_ident(
expr: &Expr,
params: &HashSet<InternedStr>,
) -> Option<(InternedStr, ExprId)> {
let mut cur = expr;
loop {
match &cur.kind {
ExprKind::Member { expr: base, .. }
| ExprKind::PtrMember { expr: base, .. }
| ExprKind::Deref(base)
| ExprKind::Cast { expr: base, .. } => cur = base,
ExprKind::StmtExpr(compound) => {
if let Some(inner) = mutable_ptr_inner_expr(compound) {
cur = inner;
} else {
return None;
}
}
ExprKind::Ident(name) if params.contains(name) => {
return Some((*name, cur.id));
}
_ => return None,
}
}
}
fn mutable_ptr_inner_expr(compound: &CompoundStmt) -> Option<&Expr> {
if compound.items.len() != 2 {
return None;
}
let decl = match &compound.items[0] {
BlockItem::Decl(d) => d,
_ => return None,
};
if decl.declarators.len() != 1 {
return None;
}
let init_decl = &decl.declarators[0];
let declared_name = init_decl.declarator.name?;
let init_expr = match init_decl.init.as_ref()? {
Initializer::Expr(e) => e.as_ref(),
_ => return None,
};
let last_expr = match &compound.items[1] {
BlockItem::Stmt(Stmt::Expr(Some(e), _)) => e,
_ => return None,
};
if let ExprKind::Ident(name) = &last_expr.kind {
if *name == declared_name {
return Some(init_expr);
}
}
None
}
fn is_anonymous_struct_or_union_field(t: &TypeRepr) -> bool {
if let TypeRepr::CType { specs, derived, .. } = t {
if !derived.is_empty() {
return false;
}
return matches!(
specs,
crate::type_repr::CTypeSpecs::Struct { name: None, .. }
);
}
false
}
fn extract_struct_name_str(t: &TypeRepr, interner: &crate::intern::StringInterner) -> Option<String> {
use crate::type_repr::{CTypeSpecs, RustTypeRepr};
match t {
TypeRepr::CType { specs, derived, .. } if derived.is_empty() => match specs {
CTypeSpecs::TypedefName(n) => Some(interner.get(*n).to_string()),
CTypeSpecs::Struct { name: Some(n), .. } => Some(interner.get(*n).to_string()),
_ => None,
},
TypeRepr::RustType { repr, .. } => match repr {
RustTypeRepr::Named(s) => Some(s.clone()),
_ => None,
},
TypeRepr::Inferred(inferred) => {
inferred.resolved_type().and_then(|t| extract_struct_name_str(t, interner))
}
_ => None,
}
}
fn wrap_with_outer_pointer(ty: TypeRepr, is_const: bool) -> TypeRepr {
use crate::type_repr::CDerivedType;
match ty {
TypeRepr::CType { specs, mut derived, source } => {
derived.push(CDerivedType::Pointer { is_const, is_volatile: false, is_restrict: false });
TypeRepr::CType { specs, derived, source }
}
other => other,
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct TypeVar(usize);
#[derive(Debug, Clone)]
pub enum TypeConstraint {
FunctionArg {
var: TypeVar,
func_name: InternedStr,
arg_index: usize,
},
HasField {
var: TypeVar,
field: InternedStr,
},
}
#[derive(Debug, Clone, PartialEq)]
pub enum Type {
Void,
Char,
SignedChar,
UnsignedChar,
Short,
UnsignedShort,
Int,
UnsignedInt,
Long,
UnsignedLong,
LongLong,
UnsignedLongLong,
Float,
Double,
LongDouble,
Bool,
Int128,
UnsignedInt128,
Pointer(Box<Type>, TypeQualifiers),
Array(Box<Type>, Option<usize>),
Function {
return_type: Box<Type>,
params: Vec<Type>,
variadic: bool,
},
Struct {
name: Option<InternedStr>,
members: Option<Vec<(InternedStr, Type)>>,
},
Union {
name: Option<InternedStr>,
members: Option<Vec<(InternedStr, Type)>>,
},
Enum {
name: Option<InternedStr>,
},
TypedefName(InternedStr),
Unknown,
}
impl Type {
pub fn display(&self, interner: &StringInterner) -> String {
match self {
Type::Void => "void".to_string(),
Type::Char => "char".to_string(),
Type::SignedChar => "signed char".to_string(),
Type::UnsignedChar => "unsigned char".to_string(),
Type::Short => "short".to_string(),
Type::UnsignedShort => "unsigned short".to_string(),
Type::Int => "int".to_string(),
Type::UnsignedInt => "unsigned int".to_string(),
Type::Long => "long".to_string(),
Type::UnsignedLong => "unsigned long".to_string(),
Type::LongLong => "long long".to_string(),
Type::UnsignedLongLong => "unsigned long long".to_string(),
Type::Float => "float".to_string(),
Type::Double => "double".to_string(),
Type::LongDouble => "long double".to_string(),
Type::Bool => "_Bool".to_string(),
Type::Int128 => "__int128".to_string(),
Type::UnsignedInt128 => "unsigned __int128".to_string(),
Type::Pointer(inner, quals) => {
let mut s = inner.display(interner);
s.push('*');
if quals.is_const {
s.push_str(" const");
}
if quals.is_volatile {
s.push_str(" volatile");
}
if quals.is_restrict {
s.push_str(" restrict");
}
s
}
Type::Array(inner, size) => {
let inner_s = inner.display(interner);
match size {
Some(n) => format!("{}[{}]", inner_s, n),
None => format!("{}[]", inner_s),
}
}
Type::Function { return_type, params, variadic } => {
let params_s: Vec<_> = params.iter()
.map(|p| p.display(interner))
.collect();
let mut s = format!("(function {} ({}))", return_type.display(interner), params_s.join(", "));
if *variadic {
s = s.replace("))", ", ...))");
}
s
}
Type::Struct { name, .. } => {
match name {
Some(n) => format!("struct {}", interner.get(*n)),
None => "struct <anonymous>".to_string(),
}
}
Type::Union { name, .. } => {
match name {
Some(n) => format!("union {}", interner.get(*n)),
None => "union <anonymous>".to_string(),
}
}
Type::Enum { name } => {
match name {
Some(n) => format!("enum {}", interner.get(*n)),
None => "enum <anonymous>".to_string(),
}
}
Type::TypedefName(name) => interner.get(*name).to_string(),
Type::Unknown => "<unknown>".to_string(),
}
}
pub fn is_integer(&self) -> bool {
matches!(
self,
Type::Char
| Type::SignedChar
| Type::UnsignedChar
| Type::Short
| Type::UnsignedShort
| Type::Int
| Type::UnsignedInt
| Type::Long
| Type::UnsignedLong
| Type::LongLong
| Type::UnsignedLongLong
| Type::Bool
| Type::Int128
| Type::UnsignedInt128
| Type::Enum { .. }
)
}
pub fn is_floating(&self) -> bool {
matches!(self, Type::Float | Type::Double | Type::LongDouble)
}
pub fn is_arithmetic(&self) -> bool {
self.is_integer() || self.is_floating()
}
pub fn is_pointer(&self) -> bool {
matches!(self, Type::Pointer(_, _))
}
pub fn to_unified(&self, interner: &StringInterner) -> UnifiedType {
match self {
Type::Void => UnifiedType::Void,
Type::Bool => UnifiedType::Bool,
Type::Char => UnifiedType::Char { signed: None },
Type::SignedChar => UnifiedType::Char { signed: Some(true) },
Type::UnsignedChar => UnifiedType::Char { signed: Some(false) },
Type::Short => UnifiedType::Int { signed: true, size: IntSize::Short },
Type::UnsignedShort => UnifiedType::Int { signed: false, size: IntSize::Short },
Type::Int => UnifiedType::Int { signed: true, size: IntSize::Int },
Type::UnsignedInt => UnifiedType::Int { signed: false, size: IntSize::Int },
Type::Long => UnifiedType::Int { signed: true, size: IntSize::Long },
Type::UnsignedLong => UnifiedType::Int { signed: false, size: IntSize::Long },
Type::LongLong => UnifiedType::Int { signed: true, size: IntSize::LongLong },
Type::UnsignedLongLong => UnifiedType::Int { signed: false, size: IntSize::LongLong },
Type::Int128 => UnifiedType::Int { signed: true, size: IntSize::Int128 },
Type::UnsignedInt128 => UnifiedType::Int { signed: false, size: IntSize::Int128 },
Type::Float => UnifiedType::Float,
Type::Double => UnifiedType::Double,
Type::LongDouble => UnifiedType::LongDouble,
Type::Pointer(inner, quals) => UnifiedType::Pointer {
inner: Box::new(inner.to_unified(interner)),
is_const: quals.is_const,
},
Type::Array(inner, size) => UnifiedType::Array {
inner: Box::new(inner.to_unified(interner)),
size: *size,
},
Type::Struct { name: Some(n), .. } => {
UnifiedType::Named(interner.get(*n).to_string())
}
Type::Struct { name: None, .. } => UnifiedType::Unknown,
Type::Union { name: Some(n), .. } => {
UnifiedType::Named(interner.get(*n).to_string())
}
Type::Union { name: None, .. } => UnifiedType::Unknown,
Type::Enum { name: Some(n) } => {
UnifiedType::Named(interner.get(*n).to_string())
}
Type::Enum { name: None } => UnifiedType::Int { signed: true, size: IntSize::Int },
Type::TypedefName(name) => {
UnifiedType::Named(interner.get(*name).to_string())
}
Type::Function { .. } => UnifiedType::Unknown,
Type::Unknown => UnifiedType::Unknown,
}
}
}
#[derive(Debug, Clone)]
pub struct Symbol {
pub name: InternedStr,
pub ty: Type,
pub loc: SourceLocation,
pub kind: SymbolKind,
}
#[derive(Debug, Clone, PartialEq)]
pub enum SymbolKind {
Variable,
Function,
Typedef,
EnumConstant(i64),
}
#[derive(Debug)]
pub struct Scope {
symbols: HashMap<InternedStr, Symbol>,
parent: Option<ScopeId>,
}
impl Scope {
fn new(parent: Option<ScopeId>) -> Self {
Self {
symbols: HashMap::new(),
parent,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct ScopeId(usize);
pub struct SemanticAnalyzer<'a> {
interner: &'a StringInterner,
scopes: Vec<Scope>,
current_scope: ScopeId,
struct_defs: HashMap<InternedStr, Vec<(InternedStr, Type)>>,
union_defs: HashMap<InternedStr, Vec<(InternedStr, Type)>>,
typedef_defs: HashMap<InternedStr, Type>,
apidoc: Option<&'a ApidocDict>,
fields_dict: Option<&'a FieldsDict>,
rust_decl_dict: Option<&'a RustDeclDict>,
inline_fn_dict: Option<&'a InlineFnDict>,
type_vars: HashMap<InternedStr, TypeVar>,
next_type_var: usize,
constraints: Vec<TypeConstraint>,
constraint_mode: bool,
macro_params: HashSet<InternedStr>,
macro_return_types: Option<&'a HashMap<String, String>>,
macro_param_types: Option<&'a HashMap<String, Vec<(String, String)>>>,
files: Option<&'a FileRegistry>,
parser_typedefs: Option<&'a HashSet<InternedStr>>,
}
impl<'a> SemanticAnalyzer<'a> {
pub fn new(
interner: &'a StringInterner,
apidoc: Option<&'a ApidocDict>,
fields_dict: Option<&'a FieldsDict>,
) -> Self {
Self::with_rust_decl_dict(interner, apidoc, fields_dict, None, None)
}
pub fn with_rust_decl_dict(
interner: &'a StringInterner,
apidoc: Option<&'a ApidocDict>,
fields_dict: Option<&'a FieldsDict>,
rust_decl_dict: Option<&'a RustDeclDict>,
inline_fn_dict: Option<&'a InlineFnDict>,
) -> Self {
let global_scope = Scope::new(None);
Self {
interner,
scopes: vec![global_scope],
current_scope: ScopeId(0),
struct_defs: HashMap::new(),
union_defs: HashMap::new(),
typedef_defs: HashMap::new(),
apidoc,
fields_dict,
rust_decl_dict,
inline_fn_dict,
type_vars: HashMap::new(),
next_type_var: 0,
constraints: Vec::new(),
constraint_mode: false,
macro_params: HashSet::new(),
macro_return_types: None,
macro_param_types: None,
files: None,
parser_typedefs: None,
}
}
pub fn set_macro_return_types(&mut self, cache: &'a HashMap<String, String>) {
self.macro_return_types = Some(cache);
}
pub fn set_macro_param_types(&mut self, cache: &'a HashMap<String, Vec<(String, String)>>) {
self.macro_param_types = Some(cache);
}
pub fn get_macro_return_type(&self, macro_name: &str) -> Option<&str> {
self.macro_return_types
.and_then(|cache| cache.get(macro_name))
.map(|s| s.as_str())
}
pub fn get_macro_param_types(&self, macro_name: &str) -> Option<&Vec<(String, String)>> {
self.macro_param_types
.and_then(|cache| cache.get(macro_name))
}
pub fn push_scope(&mut self) {
let new_scope = Scope::new(Some(self.current_scope));
let new_id = ScopeId(self.scopes.len());
self.scopes.push(new_scope);
self.current_scope = new_id;
}
pub fn pop_scope(&mut self) {
if let Some(parent) = self.scopes[self.current_scope.0].parent {
self.current_scope = parent;
}
}
pub fn define_symbol(&mut self, symbol: Symbol) {
let scope = &mut self.scopes[self.current_scope.0];
scope.symbols.insert(symbol.name, symbol);
}
pub fn lookup_symbol(&self, name: InternedStr) -> Option<&Symbol> {
let mut scope_id = Some(self.current_scope);
while let Some(id) = scope_id {
let scope = &self.scopes[id.0];
if let Some(sym) = scope.symbols.get(&name) {
return Some(sym);
}
scope_id = scope.parent;
}
None
}
pub fn begin_param_inference(&mut self, params: &[InternedStr]) {
self.constraint_mode = true;
self.type_vars.clear();
self.constraints.clear();
self.next_type_var = 0;
for ¶m in params {
let var = TypeVar(self.next_type_var);
self.next_type_var += 1;
self.type_vars.insert(param, var);
}
}
pub fn end_param_inference(&mut self) -> HashMap<InternedStr, Type> {
self.constraint_mode = false;
let solutions = self.solve_constraints();
let mut result = HashMap::new();
for (&name, &var) in &self.type_vars {
if let Some(ty) = solutions.get(&var) {
result.insert(name, ty.clone());
}
}
self.type_vars.clear();
self.constraints.clear();
self.next_type_var = 0;
result
}
fn solve_constraints(&self) -> HashMap<TypeVar, Type> {
let mut solutions = HashMap::new();
for constraint in &self.constraints {
match constraint {
TypeConstraint::FunctionArg { var, func_name, arg_index } => {
if solutions.contains_key(var) {
continue;
}
if let Some(ty) = self.lookup_rust_decl_param_type(*func_name, *arg_index) {
solutions.insert(*var, ty);
}
}
TypeConstraint::HasField { var, field } => {
if solutions.contains_key(var) {
continue;
}
if let Some(fields_dict) = self.fields_dict {
if let Some(struct_name) = fields_dict.lookup_unique(*field) {
solutions.insert(
*var,
Type::Pointer(
Box::new(Type::TypedefName(struct_name)),
TypeQualifiers::default(),
),
);
}
}
}
}
}
solutions
}
fn lookup_rust_decl_param_type(&self, func_name: InternedStr, arg_index: usize) -> Option<Type> {
let rust_decl_dict = self.rust_decl_dict?;
let func_name_str = self.interner.get(func_name);
let rust_fn = rust_decl_dict.fns.get(func_name_str)?;
let param = rust_fn.params.get(arg_index)?;
Some(self.parse_rust_type_string(¶m.ty))
}
fn lookup_inline_fn_param_type_repr(
&self,
func_name: InternedStr,
arg_index: usize,
) -> Option<TypeRepr> {
let dict = self.inline_fn_dict?;
let func_def = dict.get(func_name)?;
let param_list = func_def.declarator.derived.iter()
.find_map(|d| match d {
DerivedDecl::Function(params) => Some(params),
_ => None,
})?;
let param = param_list.params.get(arg_index)?;
let specs = CTypeSpecs::from_decl_specs(¶m.specs, self.interner);
let derived = param.declarator.as_ref()
.map(|d| {
CDerivedType::from_derived_decls(&d.derived)
.into_iter()
.take_while(|d| !matches!(d, CDerivedType::Function { .. }))
.collect()
})
.unwrap_or_default();
Some(TypeRepr::CType {
specs,
derived,
source: CTypeSource::InlineFn { func_name },
})
}
fn lookup_inline_fn_return_type_repr(&self, func_name: InternedStr) -> Option<TypeRepr> {
let dict = self.inline_fn_dict?;
let func_def = dict.get(func_name)?;
let specs = CTypeSpecs::from_decl_specs(&func_def.specs, self.interner);
let derived: Vec<_> = CDerivedType::from_derived_decls(&func_def.declarator.derived)
.into_iter()
.take_while(|d| !matches!(d, CDerivedType::Function { .. }))
.collect();
Some(TypeRepr::CType {
specs,
derived,
source: CTypeSource::InlineFn { func_name },
})
}
pub fn resolve_decl_specs(&mut self, specs: &DeclSpecs) -> Type {
let mut is_signed = false;
let mut is_unsigned = false;
let mut is_short = false;
let mut is_long = 0u8; let mut base_type: Option<Type> = None;
for spec in &specs.type_specs {
match spec {
TypeSpec::Void => base_type = Some(Type::Void),
TypeSpec::Char => base_type = Some(Type::Char),
TypeSpec::Short => is_short = true,
TypeSpec::Int => {
if base_type.is_none() {
base_type = Some(Type::Int);
}
}
TypeSpec::Long => is_long += 1,
TypeSpec::Float => base_type = Some(Type::Float),
TypeSpec::Double => base_type = Some(Type::Double),
TypeSpec::Signed => is_signed = true,
TypeSpec::Unsigned => is_unsigned = true,
TypeSpec::Bool => base_type = Some(Type::Bool),
TypeSpec::Int128 => base_type = Some(Type::Int128),
TypeSpec::Struct(s) => {
let members = self.resolve_struct_members(s);
if let (Some(name), Some(m)) = (s.name, &members) {
self.struct_defs.insert(name, m.clone());
}
base_type = Some(Type::Struct {
name: s.name,
members,
});
}
TypeSpec::Union(s) => {
let members = self.resolve_struct_members(s);
if let (Some(name), Some(m)) = (s.name, &members) {
self.union_defs.insert(name, m.clone());
}
base_type = Some(Type::Union {
name: s.name,
members,
});
}
TypeSpec::Enum(e) => {
self.process_enum(e);
base_type = Some(Type::Enum { name: e.name });
}
TypeSpec::TypedefName(name) => {
if let Some(ty) = self.typedef_defs.get(name) {
base_type = Some(ty.clone());
} else {
base_type = Some(Type::TypedefName(*name));
}
}
TypeSpec::TypeofExpr(_) => {
base_type = Some(Type::Unknown);
}
_ => {}
}
}
match (is_unsigned, is_signed, is_short, is_long, &base_type) {
(true, _, _, 0, None) | (true, _, _, 0, Some(Type::Int)) => Type::UnsignedInt,
(true, _, _, 1, _) => Type::UnsignedLong,
(true, _, _, 2, _) => Type::UnsignedLongLong,
(true, _, true, _, _) => Type::UnsignedShort,
(true, _, _, _, Some(Type::Char)) => Type::UnsignedChar,
(true, _, _, _, Some(Type::Int128)) => Type::UnsignedInt128,
(_, true, _, _, Some(Type::Char)) => Type::SignedChar,
(_, _, _, 1, None) | (_, _, _, 1, Some(Type::Int)) => Type::Long,
(_, _, _, 2, _) => Type::LongLong,
(_, _, _, 1, Some(Type::Double)) => Type::LongDouble,
(_, _, true, _, _) => Type::Short,
_ => base_type.unwrap_or(Type::Int),
}
}
fn resolve_struct_members(&mut self, spec: &StructSpec) -> Option<Vec<(InternedStr, Type)>> {
spec.members.as_ref().map(|members| {
let mut result = Vec::new();
for member in members {
let base_ty = self.resolve_decl_specs(&member.specs);
for decl in &member.declarators {
if let Some(ref d) = decl.declarator {
if let Some(name) = d.name {
let ty = self.apply_declarator(&base_ty, d);
result.push((name, ty));
}
}
}
}
result
})
}
fn process_enum(&mut self, spec: &EnumSpec) {
if let Some(ref enumerators) = spec.enumerators {
let mut value = 0i64;
for e in enumerators {
if e.value.is_some() {
value = 0; }
self.define_symbol(Symbol {
name: e.name,
ty: Type::Int,
loc: spec.loc.clone(),
kind: SymbolKind::EnumConstant(value),
});
value += 1;
}
}
}
pub fn apply_declarator(&self, base_type: &Type, decl: &Declarator) -> Type {
let mut ty = base_type.clone();
for derived in &decl.derived {
ty = match derived {
DerivedDecl::Pointer(quals) => Type::Pointer(Box::new(ty), quals.clone()),
DerivedDecl::Array(arr) => {
let _size = &arr.size;
Type::Array(Box::new(ty), None)
}
DerivedDecl::Function(params) => {
let param_types: Vec<_> = params.params
.iter()
.map(|p| {
let base = self.resolve_decl_specs_readonly(&p.specs);
if let Some(ref d) = p.declarator {
self.apply_declarator(&base, d)
} else {
base
}
})
.collect();
Type::Function {
return_type: Box::new(ty),
params: param_types,
variadic: params.is_variadic,
}
}
};
}
ty
}
fn resolve_decl_specs_readonly(&self, specs: &DeclSpecs) -> Type {
let mut is_unsigned = false;
let mut is_long = 0u8;
let mut base_type: Option<Type> = None;
for spec in &specs.type_specs {
match spec {
TypeSpec::Void => base_type = Some(Type::Void),
TypeSpec::Char => base_type = Some(Type::Char),
TypeSpec::Int => base_type = Some(Type::Int),
TypeSpec::Long => is_long += 1,
TypeSpec::Float => base_type = Some(Type::Float),
TypeSpec::Double => base_type = Some(Type::Double),
TypeSpec::Unsigned => is_unsigned = true,
TypeSpec::Bool => base_type = Some(Type::Bool),
TypeSpec::TypedefName(name) => {
if let Some(ty) = self.typedef_defs.get(name) {
base_type = Some(ty.clone());
} else {
base_type = Some(Type::TypedefName(*name));
}
}
_ => {}
}
}
match (is_unsigned, is_long, &base_type) {
(true, 0, None) | (true, 0, Some(Type::Int)) => Type::UnsignedInt,
(true, 1, _) => Type::UnsignedLong,
(_, 1, None) | (_, 1, Some(Type::Int)) => Type::Long,
(_, 2, _) => Type::LongLong,
_ => base_type.unwrap_or(Type::Int),
}
}
pub fn resolve_type_name(&self, type_name: &TypeName) -> Type {
let base_ty = self.resolve_decl_specs_readonly(&type_name.specs);
if let Some(ref abs_decl) = type_name.declarator {
self.apply_abstract_declarator(&base_ty, abs_decl)
} else {
base_ty
}
}
fn apply_abstract_declarator(&self, base_type: &Type, decl: &AbstractDeclarator) -> Type {
let mut ty = base_type.clone();
for derived in &decl.derived {
ty = match derived {
DerivedDecl::Pointer(quals) => Type::Pointer(Box::new(ty), quals.clone()),
DerivedDecl::Array(_) => {
Type::Array(Box::new(ty), None)
}
DerivedDecl::Function(params) => {
let param_types: Vec<_> = params.params
.iter()
.map(|p| {
let base = self.resolve_decl_specs_readonly(&p.specs);
if let Some(ref d) = p.declarator {
self.apply_declarator(&base, d)
} else {
base
}
})
.collect();
Type::Function {
return_type: Box::new(ty),
params: param_types,
variadic: params.is_variadic,
}
}
};
}
ty
}
pub fn process_declaration(&mut self, decl: &Declaration) {
let base_ty = self.resolve_decl_specs(&decl.specs);
if decl.specs.storage == Some(StorageClass::Typedef) {
for init_decl in &decl.declarators {
if let Some(name) = init_decl.declarator.name {
let ty = self.apply_declarator(&base_ty, &init_decl.declarator);
self.typedef_defs.insert(name, ty);
}
}
return;
}
for init_decl in &decl.declarators {
if let Some(name) = init_decl.declarator.name {
let ty = self.apply_declarator(&base_ty, &init_decl.declarator);
self.define_symbol(Symbol {
name,
ty,
loc: decl.loc().clone(),
kind: SymbolKind::Variable,
});
}
}
}
pub fn process_function_def(&mut self, func: &FunctionDef) {
let return_ty = self.resolve_decl_specs(&func.specs);
let func_ty = self.apply_declarator(&return_ty, &func.declarator);
if let Some(name) = func.declarator.name {
self.define_symbol(Symbol {
name,
ty: func_ty.clone(),
loc: func.loc().clone(),
kind: SymbolKind::Function,
});
}
self.push_scope();
if let Type::Function { params, .. } = &func_ty {
for derived in &func.declarator.derived {
if let DerivedDecl::Function(param_list) = derived {
for (param, param_ty) in param_list.params.iter().zip(params.iter()) {
if let Some(ref decl) = param.declarator {
if let Some(name) = decl.name {
self.define_symbol(Symbol {
name,
ty: param_ty.clone(),
loc: func.loc().clone(),
kind: SymbolKind::Variable,
});
}
}
}
break;
}
}
}
self.process_compound_stmt(&func.body);
self.pop_scope();
}
pub fn process_compound_stmt(&mut self, stmt: &CompoundStmt) {
self.push_scope();
for item in &stmt.items {
match item {
BlockItem::Decl(decl) => self.process_declaration(decl),
BlockItem::Stmt(stmt) => self.process_stmt(stmt),
}
}
self.pop_scope();
}
fn process_stmt(&mut self, stmt: &Stmt) {
match stmt {
Stmt::Compound(compound) => self.process_compound_stmt(compound),
Stmt::For { init, .. } => {
self.push_scope();
if let Some(ForInit::Decl(decl)) = init {
self.process_declaration(decl);
}
self.pop_scope();
}
_ => {}
}
}
pub fn set_macro_params(&mut self, params: &[InternedStr]) {
self.macro_params.clear();
for ¶m in params {
self.macro_params.insert(param);
}
}
pub fn clear_macro_params(&mut self) {
self.macro_params.clear();
}
pub fn register_macro_params_from_apidoc(
&mut self,
macro_name: InternedStr,
params: &[InternedStr],
files: &'a FileRegistry,
typedefs: &'a HashSet<InternedStr>,
) {
self.files = Some(files);
self.parser_typedefs = Some(typedefs);
self.macro_params.clear();
for ¶m in params {
self.macro_params.insert(param);
}
let macro_name_str = self.interner.get(macro_name);
if let Some(apidoc) = self.apidoc {
if let Some(entry) = apidoc.get(macro_name_str) {
for (i, ¶m_name) in params.iter().enumerate() {
if let Some(apidoc_arg) = entry.args.get(i) {
match parse_type_from_string(
&apidoc_arg.ty,
self.interner,
files,
typedefs,
) {
Ok(type_name) => {
let ty = self.resolve_type_name(&type_name);
self.define_symbol(Symbol {
name: param_name,
ty,
loc: SourceLocation::default(),
kind: SymbolKind::Variable,
});
}
Err(_) => {}
}
}
}
}
}
}
fn parse_type_string(&self, s: &str) -> TypeRepr {
if let (Some(files), Some(typedefs)) = (self.files, self.parser_typedefs) {
TypeRepr::from_c_type_string(s, self.interner, files, typedefs)
} else {
TypeRepr::from_apidoc_string(s, self.interner)
}
}
fn is_macro_param(&self, name: InternedStr) -> bool {
self.macro_params.contains(&name)
}
fn make_sv_ptr_type(&self) -> TypeRepr {
let sv_name = self.interner.lookup("SV")
.expect("SV should be interned");
TypeRepr::CType {
specs: CTypeSpecs::TypedefName(sv_name),
derived: vec![CDerivedType::Pointer { is_const: false, is_volatile: false, is_restrict: false }],
source: CTypeSource::SvFamilyCast,
}
}
fn make_sv_family_ptr_type(&self, typedef_name: InternedStr) -> TypeRepr {
TypeRepr::CType {
specs: CTypeSpecs::TypedefName(typedef_name),
derived: vec![CDerivedType::Pointer { is_const: false, is_volatile: false, is_restrict: false }],
source: CTypeSource::CommonMacroFieldInference,
}
}
fn try_infer_sv_family_from_member(
&self,
member: InternedStr,
base: &Expr,
type_env: &mut TypeEnv,
) {
let Some(fields_dict) = self.fields_dict else { return };
let Some(macro_id) = fields_dict.defining_macro_of(member) else { return };
let Some(sv_typedef) = fields_dict.sv_family_of_common_macro(macro_id) else { return };
let Some((_param_name, param_node_id)) = leftmost_param_ident(base, &self.macro_params)
else {
return;
};
let sv_type = self.make_sv_family_ptr_type(sv_typedef);
let typedef_str = self.interner.get(sv_typedef);
let member_str = self.interner.get(member);
type_env.add_constraint(TypeEnvConstraint::new(
param_node_id,
sv_type,
format!("common-macro field {} implies {}*", member_str, typedef_str),
));
}
fn get_expr_type_repr(&self, expr_id: ExprId, type_env: &TypeEnv) -> Option<TypeRepr> {
type_env.expr_constraints.get(&expr_id)
.and_then(|c| c.first())
.map(|c| c.ty.clone())
}
fn get_expr_type_repr_or_unknown(&self, expr_id: ExprId, type_env: &TypeEnv) -> TypeRepr {
self.get_expr_type_repr(expr_id, type_env)
.unwrap_or_else(|| TypeRepr::from_apidoc_string("<unknown>", self.interner))
}
fn get_expr_type_str(&self, expr_id: ExprId, type_env: &TypeEnv) -> String {
if let Some(constraints) = type_env.expr_constraints.get(&expr_id) {
if let Some(c) = constraints.first() {
return c.ty.to_display_string(self.interner);
}
}
"<unknown>".to_string()
}
fn compute_binary_type_str(&self, op: &BinOp, lhs_id: ExprId, rhs_id: ExprId, type_env: &TypeEnv) -> String {
match op {
BinOp::Lt | BinOp::Gt | BinOp::Le | BinOp::Ge |
BinOp::Eq | BinOp::Ne | BinOp::LogAnd | BinOp::LogOr => "int".to_string(),
_ => {
let lhs_ty = self.get_expr_type_str(lhs_id, type_env);
let rhs_ty = self.get_expr_type_str(rhs_id, type_env);
self.usual_arithmetic_conversion_str(&lhs_ty, &rhs_ty)
}
}
}
fn usual_arithmetic_conversion_str(&self, lhs: &str, rhs: &str) -> String {
let is_ptr = |ty: &str| ty.contains('*');
if is_ptr(lhs) && !is_ptr(rhs) {
return lhs.to_string();
}
if is_ptr(rhs) && !is_ptr(lhs) {
return rhs.to_string();
}
let rank = |ty: &str| -> u8 {
match ty {
"long double" => 10,
"double" => 9,
"float" => 8,
"unsigned long long" => 7,
"long long" => 6,
"unsigned long" => 5,
"long" => 4,
"unsigned int" => 3,
"int" => 2,
"unsigned short" => 1,
"short" => 1,
_ => 0,
}
};
if rank(lhs) >= rank(rhs) {
lhs.to_string()
} else {
rhs.to_string()
}
}
fn compute_conditional_type_str(&self, then_id: ExprId, else_id: ExprId, type_env: &TypeEnv) -> String {
let then_ty = self.get_expr_type_str(then_id, type_env);
let else_ty = self.get_expr_type_str(else_id, type_env);
let is_void_ptr = |s: &str| s.contains("void") && s.contains('*');
let is_concrete_ptr = |s: &str| !s.contains("void") && s.contains('*');
if is_void_ptr(&then_ty) && is_concrete_ptr(&else_ty) {
return else_ty;
}
if is_void_ptr(&else_ty) && is_concrete_ptr(&then_ty) {
return then_ty;
}
self.usual_arithmetic_conversion_str(&then_ty, &else_ty)
}
pub fn collect_stmt_constraints(&mut self, stmt: &Stmt, type_env: &mut TypeEnv) {
match stmt {
Stmt::Compound(compound) => {
for item in &compound.items {
match item {
BlockItem::Stmt(s) => self.collect_stmt_constraints(s, type_env),
BlockItem::Decl(_) => {} }
}
}
Stmt::Expr(Some(expr), _) => {
self.collect_expr_constraints(expr, type_env);
}
Stmt::If { cond, then_stmt, else_stmt, .. } => {
self.collect_expr_constraints(cond, type_env);
self.collect_stmt_constraints(then_stmt, type_env);
if let Some(else_s) = else_stmt {
self.collect_stmt_constraints(else_s, type_env);
}
}
Stmt::While { cond, body, .. } => {
self.collect_expr_constraints(cond, type_env);
self.collect_stmt_constraints(body, type_env);
}
Stmt::DoWhile { body, cond, .. } => {
self.collect_stmt_constraints(body, type_env);
self.collect_expr_constraints(cond, type_env);
}
Stmt::For { init, cond, step, body, .. } => {
if let Some(ForInit::Expr(e)) = init {
self.collect_expr_constraints(e, type_env);
}
if let Some(c) = cond {
self.collect_expr_constraints(c, type_env);
}
if let Some(s) = step {
self.collect_expr_constraints(s, type_env);
}
self.collect_stmt_constraints(body, type_env);
}
Stmt::Return(Some(expr), _) => {
self.collect_expr_constraints(expr, type_env);
}
Stmt::Switch { expr, body, .. } => {
self.collect_expr_constraints(expr, type_env);
self.collect_stmt_constraints(body, type_env);
}
Stmt::Case { expr, stmt, .. } => {
self.collect_expr_constraints(expr, type_env);
self.collect_stmt_constraints(stmt, type_env);
}
Stmt::Default { stmt, .. } | Stmt::Label { stmt, .. } => {
self.collect_stmt_constraints(stmt, type_env);
}
_ => {} }
}
fn resolve_typedef_to_struct_name<'b>(&self, name: &'b str) -> std::borrow::Cow<'b, str> {
let Some(rd) = self.rust_decl_dict else {
return std::borrow::Cow::Borrowed(name);
};
let mut current = std::borrow::Cow::Borrowed(name);
for _ in 0..8 {
if rd.structs.contains_key(current.as_ref()) {
return current;
}
let Some(alias) = rd.types.get(current.as_ref()) else {
return current;
};
current = std::borrow::Cow::Owned(alias.ty.clone());
}
current
}
fn replace_anonymous_with_bindings(
&self,
parent_struct_name_str: &str,
field_name_str: &str,
c_field_type: TypeRepr,
) -> TypeRepr {
if !is_anonymous_struct_or_union_field(&c_field_type) {
return c_field_type;
}
let Some(rd) = self.rust_decl_dict else {
return c_field_type;
};
let resolved = self.resolve_typedef_to_struct_name(parent_struct_name_str);
let Some(rust_struct) = rd.structs.get(resolved.as_ref()) else {
return c_field_type;
};
let Some(rust_field) = rust_struct.fields.iter().find(|f| f.name == field_name_str) else {
return c_field_type;
};
TypeRepr::from_unified_type(&rust_field.uty, self.interner)
}
fn lookup_field_in_bindings(
&self,
parent_struct_name_str: &str,
field_name_str: &str,
) -> Option<TypeRepr> {
let rd = self.rust_decl_dict?;
let resolved = self.resolve_typedef_to_struct_name(parent_struct_name_str);
let rust_struct = rd.structs.get(resolved.as_ref())?;
let rust_field = rust_struct.fields.iter().find(|f| f.name == field_name_str)?;
Some(TypeRepr::from_unified_type(&rust_field.uty, self.interner))
}
pub fn collect_expr_constraints(&mut self, expr: &Expr, type_env: &mut TypeEnv) {
match &expr.kind {
ExprKind::IntLit(_) => {
type_env.add_constraint(TypeEnvConstraint::new(
expr.id,
TypeRepr::Inferred(InferredType::IntLiteral),
"integer literal",
));
}
ExprKind::UIntLit(_) => {
type_env.add_constraint(TypeEnvConstraint::new(
expr.id,
TypeRepr::Inferred(InferredType::UIntLiteral),
"unsigned integer literal",
));
}
ExprKind::FloatLit(_) => {
type_env.add_constraint(TypeEnvConstraint::new(
expr.id,
TypeRepr::Inferred(InferredType::FloatLiteral),
"float literal",
));
}
ExprKind::CharLit(_) => {
type_env.add_constraint(TypeEnvConstraint::new(
expr.id,
TypeRepr::Inferred(InferredType::CharLiteral),
"char literal",
));
}
ExprKind::StringLit(_) => {
type_env.add_constraint(TypeEnvConstraint::new(
expr.id,
TypeRepr::Inferred(InferredType::StringLiteral),
"string literal",
));
}
ExprKind::Ident(name) => {
let name_str = self.interner.get(*name);
if let Some(sym) = self.lookup_symbol(*name) {
let ty_str = sym.ty.display(self.interner);
let resolved = TypeRepr::from_apidoc_string(&ty_str, self.interner);
type_env.add_constraint(TypeEnvConstraint::new(
expr.id,
TypeRepr::Inferred(InferredType::SymbolLookup {
name: *name,
resolved_type: Box::new(resolved),
}),
"symbol lookup",
));
} else if let Some(rust_decl_dict) = self.rust_decl_dict {
if let Some(rust_const) = rust_decl_dict.lookup_const(name_str) {
type_env.add_constraint(TypeEnvConstraint::new(
expr.id,
TypeRepr::RustType {
repr: RustTypeRepr::from_type_string(&rust_const.ty),
source: RustTypeSource::Const {
const_name: name_str.to_string(),
},
},
"bindings constant",
));
} else if name_str == "my_perl" {
type_env.add_constraint(TypeEnvConstraint::new(
expr.id,
TypeRepr::Inferred(InferredType::ThxDefault),
"THX default type",
));
}
} else if name_str == "my_perl" {
type_env.add_constraint(TypeEnvConstraint::new(
expr.id,
TypeRepr::Inferred(InferredType::ThxDefault),
"THX default type",
));
}
if self.is_macro_param(*name) {
type_env.link_expr_to_param(expr.id, *name, "parameter reference");
}
}
ExprKind::Call { func, args } => {
self.collect_expr_constraints(func, type_env);
for arg in args {
self.collect_expr_constraints(arg, type_env);
}
self.collect_call_constraints(expr.id, func, args, type_env);
}
ExprKind::Binary { op, lhs, rhs } => {
self.collect_expr_constraints(lhs, type_env);
self.collect_expr_constraints(rhs, type_env);
let result_ty_str = self.compute_binary_type_str(op, lhs.id, rhs.id, type_env);
let result_type = TypeRepr::from_apidoc_string(&result_ty_str, self.interner);
type_env.add_constraint(TypeEnvConstraint::new(
expr.id,
TypeRepr::Inferred(InferredType::BinaryOp {
op: *op,
result_type: Box::new(result_type),
}),
"binary expression",
));
}
ExprKind::Conditional { cond, then_expr, else_expr } => {
self.collect_expr_constraints(cond, type_env);
self.collect_expr_constraints(then_expr, type_env);
self.collect_expr_constraints(else_expr, type_env);
let then_type = self.get_expr_type_repr_or_unknown(then_expr.id, type_env);
let else_type = self.get_expr_type_repr_or_unknown(else_expr.id, type_env);
let result_ty_str = self.compute_conditional_type_str(then_expr.id, else_expr.id, type_env);
let result_type = TypeRepr::from_apidoc_string(&result_ty_str, self.interner);
type_env.add_constraint(TypeEnvConstraint::new(
expr.id,
TypeRepr::Inferred(InferredType::Conditional {
then_type: Box::new(then_type),
else_type: Box::new(else_type),
result_type: Box::new(result_type),
}),
"conditional expression",
));
}
ExprKind::Cast { type_name, expr: inner } => {
self.collect_expr_constraints(inner, type_env);
let specs = CTypeSpecs::from_decl_specs(&type_name.specs, self.interner);
let derived: Vec<CDerivedType> = type_name.declarator.as_ref()
.map(|d| {
CDerivedType::from_derived_decls(&d.derived)
.into_iter()
.take_while(|d| !matches!(d, CDerivedType::Function { .. }))
.collect()
})
.unwrap_or_default();
let target_type = TypeRepr::CType {
specs: specs.clone(),
derived: derived.clone(),
source: CTypeSource::Cast,
};
type_env.add_constraint(TypeEnvConstraint::new(
expr.id,
TypeRepr::Inferred(InferredType::Cast {
target_type: Box::new(target_type),
}),
"cast expression",
));
if let Some(fields_dict) = self.fields_dict {
let is_single_ptr = derived.len() == 1
&& matches!(derived[0], CDerivedType::Pointer { .. });
if is_single_ptr {
if let Some(type_name_id) = specs.type_name() {
if fields_dict.is_sv_family_type(type_name_id) {
if let ExprKind::Ident(param_name) = &inner.kind {
if self.is_macro_param(*param_name) {
let sv_type = self.make_sv_ptr_type();
type_env.add_constraint(TypeEnvConstraint::new(
inner.id,
sv_type,
"SV family cast",
));
}
}
}
}
}
}
}
ExprKind::Index { expr: base, index } => {
self.collect_expr_constraints(base, type_env);
self.collect_expr_constraints(index, type_env);
let base_ty_str = self.get_expr_type_str(base.id, type_env);
let elem_ty_str = if base_ty_str.ends_with('*') {
base_ty_str.trim_end_matches('*').trim().to_string()
} else if base_ty_str.contains('[') {
base_ty_str.split('[').next().unwrap_or(&base_ty_str).trim().to_string()
} else {
"<unknown>".to_string()
};
let base_type = TypeRepr::from_apidoc_string(&base_ty_str, self.interner);
let element_type = TypeRepr::from_apidoc_string(&elem_ty_str, self.interner);
type_env.add_constraint(TypeEnvConstraint::new(
expr.id,
TypeRepr::Inferred(InferredType::ArraySubscript {
base_type: Box::new(base_type),
element_type: Box::new(element_type),
}),
"array subscript",
));
}
ExprKind::Member { expr: base, member } => {
self.collect_expr_constraints(base, type_env);
let base_ty = self.get_expr_type_str(base.id, type_env);
let member_name = self.interner.get(*member);
let field_type = if self.is_sv_u_access(base) {
self.lookup_sv_u_field_type(*member)
.map(|c_type| Box::new(TypeRepr::from_apidoc_string(&c_type, self.interner)))
} else {
let base_type_repr = self.get_expr_type_repr(base.id, type_env);
let struct_name_id = base_type_repr.as_ref().and_then(|t| t.type_name());
let struct_name_str = base_type_repr.as_ref()
.and_then(|t| extract_struct_name_str(t, self.interner));
let field_str = self.interner.get(*member);
let flex_ptr = struct_name_id.and_then(|n| {
self.fields_dict?
.flexible_array_element(n, *member)
.map(|elem| Box::new(wrap_with_outer_pointer(elem.clone(), false)))
});
let direct = flex_ptr.or_else(|| {
struct_name_id.and_then(|n| {
self.fields_dict?.get_field_type(n, *member).map(|ft| {
let parent_str = self.interner.get(n);
let patched = self.replace_anonymous_with_bindings(
parent_str, field_str, ft.type_repr.clone(),
);
Box::new(patched)
})
})
});
let direct = direct.or_else(|| {
struct_name_str.as_deref().and_then(|parent_str| {
self.lookup_field_in_bindings(parent_str, field_str)
.map(Box::new)
})
});
direct.or_else(|| {
self.fields_dict
.and_then(|fd| fd.rust_type_of_common_field(*member))
.cloned()
.map(Box::new)
})
};
type_env.add_constraint(TypeEnvConstraint::new(
expr.id,
TypeRepr::Inferred(InferredType::MemberAccess {
base_type: base_ty.clone(),
member: *member,
field_type,
}),
format!("{}.{}", base_ty, member_name),
));
self.try_infer_sv_family_from_member(*member, base, type_env);
}
ExprKind::PtrMember { expr: base, member } => {
self.collect_expr_constraints(base, type_env);
let base_ty = self.get_expr_type_str(base.id, type_env);
let member_name = self.interner.get(*member);
if let Some(fields_dict) = self.fields_dict {
if base_ty == "/* unknown */" || self.is_ident_expr(base) {
let inferred_struct = fields_dict.lookup_unique(*member)
.or_else(|| fields_dict.get_consistent_base_type(*member, self.interner));
if let Some(struct_name) = inferred_struct {
let type_name = fields_dict.get_typedef_for_struct(struct_name)
.unwrap_or(struct_name);
let type_name_str = self.interner.get(type_name);
let base_type = TypeRepr::CType {
specs: CTypeSpecs::TypedefName(type_name),
derived: vec![CDerivedType::Pointer {
is_const: false,
is_volatile: false,
is_restrict: false,
}],
source: CTypeSource::FieldInference { field_name: *member },
};
type_env.add_constraint(TypeEnvConstraint::new(
base.id,
base_type,
format!("field {} implies {}*", member_name, type_name_str),
));
}
}
}
let base_type_repr = self.get_expr_type_repr(base.id, type_env);
let pointee = base_type_repr.as_ref().and_then(|t| t.pointee_name());
let (field_type, used_consistent_type) = if let Some(name) = pointee {
let flex_ptr = self.fields_dict.and_then(|fd| {
fd.flexible_array_element(name, *member)
.map(|elem| Box::new(wrap_with_outer_pointer(elem.clone(), false)))
});
let parent_str = self.interner.get(name);
let field_str = self.interner.get(*member);
let direct = flex_ptr.or_else(|| {
self.fields_dict
.and_then(|fd| fd.get_field_type(name, *member))
.map(|ft| {
let patched = self.replace_anonymous_with_bindings(
parent_str, field_str, ft.type_repr.clone(),
);
Box::new(patched)
})
});
let direct = direct.or_else(|| {
self.lookup_field_in_bindings(parent_str, field_str)
.map(Box::new)
});
let ty = direct.or_else(|| {
self.fields_dict
.and_then(|fd| fd.rust_type_of_common_field(*member))
.cloned()
.map(Box::new)
});
(ty, false)
} else if let Some(fields_dict) = self.fields_dict {
let consistent = fields_dict.get_consistent_field_type(*member)
.cloned()
.map(Box::new);
let ty = consistent.or_else(|| {
fields_dict.rust_type_of_common_field(*member)
.cloned()
.map(Box::new)
});
(ty, true)
} else {
(None, false)
};
type_env.add_constraint(TypeEnvConstraint::new(
expr.id,
TypeRepr::Inferred(InferredType::PtrMemberAccess {
base_type: base_ty.clone(),
member: *member,
field_type,
used_consistent_type,
}),
format!("{}->{}", base_ty, member_name),
));
self.try_infer_sv_family_from_member(*member, base, type_env);
}
ExprKind::Assign { lhs, rhs, .. } => {
self.collect_expr_constraints(lhs, type_env);
self.collect_expr_constraints(rhs, type_env);
let lhs_type = self.get_expr_type_repr_or_unknown(lhs.id, type_env);
type_env.add_constraint(TypeEnvConstraint::new(
expr.id,
TypeRepr::Inferred(InferredType::Assignment {
lhs_type: Box::new(lhs_type),
}),
"assignment expression",
));
}
ExprKind::Comma { lhs, rhs } => {
self.collect_expr_constraints(lhs, type_env);
self.collect_expr_constraints(rhs, type_env);
let rhs_type = self.get_expr_type_repr_or_unknown(rhs.id, type_env);
type_env.add_constraint(TypeEnvConstraint::new(
expr.id,
TypeRepr::Inferred(InferredType::Comma {
rhs_type: Box::new(rhs_type),
}),
"comma expression",
));
}
ExprKind::PreInc(inner) | ExprKind::PreDec(inner) |
ExprKind::PostInc(inner) | ExprKind::PostDec(inner) => {
self.collect_expr_constraints(inner, type_env);
let inner_type = self.get_expr_type_repr_or_unknown(inner.id, type_env);
type_env.add_constraint(TypeEnvConstraint::new(
expr.id,
TypeRepr::Inferred(InferredType::IncDec {
inner_type: Box::new(inner_type),
}),
"increment/decrement",
));
}
ExprKind::AddrOf(inner) => {
self.collect_expr_constraints(inner, type_env);
let inner_type = self.get_expr_type_repr_or_unknown(inner.id, type_env);
type_env.add_constraint(TypeEnvConstraint::new(
expr.id,
TypeRepr::Inferred(InferredType::AddressOf {
inner_type: Box::new(inner_type),
}),
"address-of",
));
}
ExprKind::Deref(inner) => {
self.collect_expr_constraints(inner, type_env);
let pointer_type = self.get_expr_type_repr_or_unknown(inner.id, type_env);
type_env.add_constraint(TypeEnvConstraint::new(
expr.id,
TypeRepr::Inferred(InferredType::Dereference {
pointer_type: Box::new(pointer_type),
}),
"dereference",
));
}
ExprKind::UnaryPlus(inner) | ExprKind::UnaryMinus(inner) => {
self.collect_expr_constraints(inner, type_env);
let inner_type = self.get_expr_type_repr_or_unknown(inner.id, type_env);
type_env.add_constraint(TypeEnvConstraint::new(
expr.id,
TypeRepr::Inferred(InferredType::UnaryArithmetic {
inner_type: Box::new(inner_type),
}),
"unary plus/minus",
));
}
ExprKind::BitNot(inner) => {
self.collect_expr_constraints(inner, type_env);
let inner_type = self.get_expr_type_repr_or_unknown(inner.id, type_env);
type_env.add_constraint(TypeEnvConstraint::new(
expr.id,
TypeRepr::Inferred(InferredType::UnaryArithmetic {
inner_type: Box::new(inner_type),
}),
"bitwise not",
));
}
ExprKind::LogNot(inner) => {
self.collect_expr_constraints(inner, type_env);
type_env.add_constraint(TypeEnvConstraint::new(
expr.id,
TypeRepr::Inferred(InferredType::LogicalNot),
"logical not",
));
}
ExprKind::Sizeof(inner) => {
self.collect_expr_constraints(inner, type_env);
type_env.add_constraint(TypeEnvConstraint::new(
expr.id,
TypeRepr::Inferred(InferredType::Sizeof),
"sizeof expression",
));
}
ExprKind::SizeofType(_) => {
type_env.add_constraint(TypeEnvConstraint::new(
expr.id,
TypeRepr::Inferred(InferredType::Sizeof),
"sizeof type",
));
}
ExprKind::Alignof(_) => {
type_env.add_constraint(TypeEnvConstraint::new(
expr.id,
TypeRepr::Inferred(InferredType::Alignof),
"alignof",
));
}
ExprKind::CompoundLit { type_name, .. } => {
let ty = self.resolve_type_name(type_name);
let ty_str = ty.display(self.interner);
let type_name_repr = TypeRepr::from_apidoc_string(&ty_str, self.interner);
type_env.add_constraint(TypeEnvConstraint::new(
expr.id,
TypeRepr::Inferred(InferredType::CompoundLiteral {
type_name: Box::new(type_name_repr),
}),
"compound literal",
));
}
ExprKind::StmtExpr(compound) => {
self.collect_compound_constraints(compound, type_env);
if let Some(last_expr_id) = self.get_last_expr_id(compound) {
let last_expr_type = self.get_expr_type_repr_or_unknown(last_expr_id, type_env);
type_env.add_constraint(TypeEnvConstraint::new(
expr.id,
TypeRepr::Inferred(InferredType::StmtExpr {
last_expr_type: Some(Box::new(last_expr_type)),
}),
"statement expression",
));
} else {
type_env.add_constraint(TypeEnvConstraint::new(
expr.id,
TypeRepr::Inferred(InferredType::StmtExpr {
last_expr_type: None,
}),
"statement expression (empty)",
));
}
}
ExprKind::Assert { condition, .. } => {
self.collect_expr_constraints(condition, type_env);
type_env.add_constraint(TypeEnvConstraint::new(
expr.id,
TypeRepr::Inferred(InferredType::Assert),
"assertion",
));
}
ExprKind::BuiltinCall { name, args } => {
for arg in args {
if let crate::ast::BuiltinArg::Expr(e) = arg {
self.collect_expr_constraints(e, type_env);
}
}
let func_name = self.interner.get(*name);
if func_name == "offsetof" || func_name == "__builtin_offsetof"
|| func_name == "STRUCT_OFFSET"
{
type_env.add_constraint(TypeEnvConstraint::new(
expr.id,
TypeRepr::Inferred(InferredType::Sizeof),
"offsetof returns size_t",
));
}
}
ExprKind::MacroCall { name, args, expanded, .. } => {
for arg in args {
self.collect_expr_constraints(arg, type_env);
}
let macro_name_str = self.interner.get(*name);
if let Some(param_types) = self.get_macro_param_types(macro_name_str) {
for (i, arg) in args.iter().enumerate() {
if let Some((param_name, type_str)) = param_types.get(i) {
let constraint = TypeEnvConstraint::new(
arg.id,
TypeRepr::from_rust_string(type_str),
format!("arg {} ({}) of macro {}()", i, param_name, macro_name_str),
);
type_env.add_constraint(constraint);
}
}
}
self.collect_expr_constraints(expanded, type_env);
if let Some(constraints) = type_env.get_expr_constraints(expanded.id) {
if let Some(constraint) = constraints.first() {
type_env.add_constraint(TypeEnvConstraint::new(
expr.id,
constraint.ty.clone(),
"macro call (expanded)",
));
}
}
}
}
}
fn get_last_expr_id(&self, compound: &CompoundStmt) -> Option<ExprId> {
if let Some(BlockItem::Stmt(Stmt::Expr(Some(expr), _))) = compound.items.last() {
Some(expr.id)
} else {
None
}
}
fn is_sv_u_access(&self, base: &Expr) -> bool {
if let ExprKind::PtrMember { member, .. } = &base.kind {
let sv_u_id = self.interner.lookup("sv_u");
sv_u_id.map_or(false, |id| *member == id)
} else {
false
}
}
fn is_ident_expr(&self, expr: &Expr) -> bool {
matches!(expr.kind, ExprKind::Ident(_))
}
fn lookup_sv_u_field_type(&self, field: InternedStr) -> Option<String> {
self.fields_dict?
.get_sv_u_field_type(field)
.map(|s| s.to_string())
}
fn collect_call_constraints(
&mut self,
call_expr_id: ExprId,
func: &Expr,
args: &[Expr],
type_env: &mut TypeEnv,
) {
let func_name = match &func.kind {
ExprKind::Ident(name) => *name,
_ => return, };
let func_name_str = self.interner.get(func_name);
if let Some(rust_decl_dict) = self.rust_decl_dict {
if let Some(rust_fn) = rust_decl_dict.fns.get(func_name_str) {
for (i, arg) in args.iter().enumerate() {
if let Some(param) = rust_fn.params.get(i) {
let constraint = TypeEnvConstraint::new(
arg.id,
TypeRepr::RustType {
repr: RustTypeRepr::from_type_string(¶m.ty),
source: RustTypeSource::FnParam {
func_name: func_name_str.to_string(),
param_index: i,
},
},
format!("arg {} of {}()", i, func_name_str),
);
type_env.add_constraint(constraint);
}
}
if let Some(ref ret_ty) = rust_fn.ret_ty {
let return_constraint = TypeEnvConstraint::new(
call_expr_id,
TypeRepr::RustType {
repr: RustTypeRepr::from_type_string(ret_ty),
source: RustTypeSource::FnReturn {
func_name: func_name_str.to_string(),
},
},
format!("return type of {}()", func_name_str),
);
type_env.add_constraint(return_constraint);
}
}
}
if let Some(apidoc) = self.apidoc {
if let Some(entry) = apidoc.get(func_name_str) {
for (i, arg) in args.iter().enumerate() {
if let Some(apidoc_arg) = entry.args.get(i) {
let constraint = TypeEnvConstraint::new(
arg.id,
self.parse_type_string(&apidoc_arg.ty),
format!("arg {} ({}) of {}()", i, apidoc_arg.name, func_name_str),
);
type_env.add_constraint(constraint);
}
}
if let Some(ref return_type) = entry.return_type {
let return_constraint = TypeEnvConstraint::new(
call_expr_id,
self.parse_type_string(return_type),
format!("return type of {}()", func_name_str),
);
type_env.add_constraint(return_constraint);
}
}
}
if self.inline_fn_dict.is_some() {
for (i, arg) in args.iter().enumerate() {
if let Some(type_repr) = self.lookup_inline_fn_param_type_repr(func_name, i) {
let constraint = TypeEnvConstraint::new(
arg.id,
type_repr,
format!("arg {} of inline {}()", i, func_name_str),
);
type_env.add_constraint(constraint);
}
}
if let Some(type_repr) = self.lookup_inline_fn_return_type_repr(func_name) {
let return_constraint = TypeEnvConstraint::new(
call_expr_id,
type_repr,
format!("return type of inline {}()", func_name_str),
);
type_env.add_constraint(return_constraint);
}
}
if let Some(param_types) = self.get_macro_param_types(func_name_str) {
for (i, arg) in args.iter().enumerate() {
if let Some((param_name, type_str)) = param_types.get(i) {
let constraint = TypeEnvConstraint::new(
arg.id,
TypeRepr::from_rust_string(type_str),
format!("arg {} ({}) of macro {}()", i, param_name, func_name_str),
);
type_env.add_constraint(constraint);
}
}
}
if let Some(return_type_str) = self.get_macro_return_type(func_name_str) {
let return_constraint = TypeEnvConstraint::new(
call_expr_id,
TypeRepr::from_rust_string(return_type_str),
format!("return type of macro {}()", func_name_str),
);
type_env.add_constraint(return_constraint);
}
}
fn collect_compound_constraints(&mut self, compound: &CompoundStmt, type_env: &mut TypeEnv) {
for item in &compound.items {
match item {
BlockItem::Decl(decl) => {
self.collect_decl_initializer_constraints(decl, type_env);
}
BlockItem::Stmt(Stmt::Expr(Some(expr), _)) => {
self.collect_expr_constraints(expr, type_env);
}
BlockItem::Stmt(Stmt::Return(Some(expr), _)) => {
self.collect_expr_constraints(expr, type_env);
}
BlockItem::Stmt(Stmt::Compound(inner)) => {
self.collect_compound_constraints(inner, type_env);
}
_ => {}
}
}
}
fn collect_decl_initializer_constraints(&mut self, decl: &Declaration, type_env: &mut TypeEnv) {
for init_decl in &decl.declarators {
if let Some(ref init) = init_decl.init {
self.collect_initializer_constraints(init, type_env);
}
}
}
fn collect_initializer_constraints(&mut self, init: &Initializer, type_env: &mut TypeEnv) {
match init {
Initializer::Expr(expr) => {
self.collect_expr_constraints(expr, type_env);
}
Initializer::List(items) => {
for item in items {
self.collect_initializer_constraints(&item.init, type_env);
}
}
}
}
fn parse_rust_type_string(&self, type_str: &str) -> Type {
let normalized = type_str
.replace("* mut", "*mut")
.replace("* const", "*const");
let trimmed = normalized.trim();
if let Some(rest) = trimmed.strip_prefix("*mut ") {
return Type::Pointer(
Box::new(self.parse_rust_type_string(rest)),
TypeQualifiers::default(),
);
}
if let Some(rest) = trimmed.strip_prefix("*const ") {
return Type::Pointer(
Box::new(self.parse_rust_type_string(rest)),
TypeQualifiers { is_const: true, ..Default::default() },
);
}
match trimmed {
"()" => Type::Void,
"c_char" => Type::Char,
"c_int" => Type::Int,
"c_uint" => Type::UnsignedInt,
"c_long" => Type::Long,
"c_ulong" => Type::UnsignedLong,
"bool" => Type::Bool,
"usize" => Type::UnsignedLong,
"isize" => Type::Long,
_ => {
if let Some(interned) = self.interner.lookup(trimmed) {
Type::TypedefName(interned)
} else {
Type::Unknown
}
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_type_display() {
let interner = StringInterner::new();
assert_eq!(Type::Int.display(&interner), "int");
assert_eq!(Type::UnsignedLong.display(&interner), "unsigned long");
assert_eq!(
Type::Pointer(Box::new(Type::Char), TypeQualifiers::default()).display(&interner),
"char*"
);
}
#[test]
fn test_scope_management() {
let mut interner = StringInterner::new();
let x = interner.intern("x");
let mut analyzer = SemanticAnalyzer::new(&interner, None, None);
analyzer.define_symbol(Symbol {
name: x,
ty: Type::Int,
loc: SourceLocation::default(),
kind: SymbolKind::Variable,
});
assert!(analyzer.lookup_symbol(x).is_some());
analyzer.push_scope();
assert!(analyzer.lookup_symbol(x).is_some());
analyzer.define_symbol(Symbol {
name: x,
ty: Type::Float, loc: SourceLocation::default(),
kind: SymbolKind::Variable,
});
let sym = analyzer.lookup_symbol(x).unwrap();
assert_eq!(sym.ty, Type::Float);
analyzer.pop_scope();
let sym = analyzer.lookup_symbol(x).unwrap();
assert_eq!(sym.ty, Type::Int);
}
}