diff --git a/c2rust-transpile/src/c_ast/c_decl.rs b/c2rust-transpile/src/c_ast/c_decl.rs new file mode 100644 index 0000000000..05b3f34b08 --- /dev/null +++ b/c2rust-transpile/src/c_ast/c_decl.rs @@ -0,0 +1,398 @@ +use crate::c_ast::c_expr::{CExprId, ConstIntExpr}; +use crate::c_ast::c_stmt::CStmtId; +use crate::c_ast::c_type::{CFuncTypeId, CQualTypeId, CTypeKind}; +use crate::c_ast::{Attribute, Located, SomeId, SrcLoc, TypedAstContext}; +use indexmap::{IndexMap, IndexSet}; +use std::fmt::Debug; +use std::ops::Index; + +#[derive(Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Copy, Clone)] +pub struct CDeclId(pub u64); + +// These are references into particular variants of AST nodes +pub type CFieldId = CDeclId; // Records always contain 'DeclKind::Field's +pub type CParamId = CDeclId; // Parameters always contain 'DeclKind::Variable's +pub type CRecordId = CDeclId; // Record types need to point to 'DeclKind::Record' +pub type CTypedefId = CDeclId; // Typedef types need to point to 'DeclKind::Typedef' +pub type CEnumId = CDeclId; // Enum types need to point to 'DeclKind::Enum' +pub type CEnumConstantId = CDeclId; // Enum's need to point to child 'DeclKind::EnumConstant's + +#[derive(Debug, Clone)] +pub enum CDeclKind { + // http://clang.llvm.org/doxygen/classclang_1_1FunctionDecl.html + Function { + is_global: bool, + is_inline: bool, + is_implicit: bool, + is_extern: bool, + is_inline_externally_visible: bool, + typ: CFuncTypeId, + name: String, + parameters: Vec, + body: Option, + attrs: IndexSet, + }, + + // http://clang.llvm.org/doxygen/classclang_1_1VarDecl.html + Variable { + has_static_duration: bool, + has_thread_duration: bool, + is_externally_visible: bool, + is_defn: bool, + ident: String, + initializer: Option, + typ: CQualTypeId, + attrs: IndexSet, + }, + + // Enum (http://clang.llvm.org/doxygen/classclang_1_1EnumDecl.html) + Enum { + name: Option, + variants: Vec, + integral_type: Option, + }, + + EnumConstant { + name: String, + value: ConstIntExpr, + }, + + // Typedef + Typedef { + name: String, + typ: CQualTypeId, + is_implicit: bool, + target_dependent_macro: Option, + }, + + // Struct + Struct { + name: Option, + fields: Option>, + is_packed: bool, + manual_alignment: Option, + max_field_alignment: Option, + platform_byte_size: u64, + platform_alignment: u64, + }, + + // Union + Union { + name: Option, + fields: Option>, + is_packed: bool, + }, + + // Field + Field { + name: String, + typ: CQualTypeId, + bitfield_width: Option, + platform_bit_offset: u64, + platform_type_bitwidth: u64, + }, + + MacroObject { + name: String, + // replacements: Vec, + }, + + MacroFunction { + name: String, + // replacements: Vec, + }, + + NonCanonicalDecl { + canonical_decl: CDeclId, + }, + + StaticAssert { + assert_expr: CExprId, + message: Option, + }, +} + +pub type CDecl = Located; + +impl CDeclKind { + pub fn get_name(&self) -> Option<&String> { + use CDeclKind::*; + Some(match self { + Function { name: i, .. } => i, + Variable { ident: i, .. } => i, + Typedef { name: i, .. } => i, + EnumConstant { name: i, .. } => i, + Enum { name: Some(i), .. } => i, + Struct { name: Some(i), .. } => i, + Union { name: Some(i), .. } => i, + Field { name: i, .. } => i, + MacroObject { name, .. } => name, + _ => return None, + }) + } +} + +/// The range of source code locations for a C declaration. +#[derive(Copy, Clone)] +pub struct CDeclSrcRange { + /// The earliest position where this declaration or its documentation might start. + pub earliest_begin: SrcLoc, + /// A position by which this declaration itself is known to have begun. + /// Attributes or return type may possibly precede this position. + pub strict_begin: SrcLoc, + /// The end of the declaration, except for possible trailing semicolon. + pub end: SrcLoc, +} + +impl TypedAstContext { + pub(super) fn add_decl_parents(&mut self, id: CDeclId, kind: &CDeclKind) { + use CDeclKind::*; + let parent = SomeId::Decl(id); + + match *kind { + Function { + ref parameters, + body, + .. + } => { + for ¶meter in parameters { + self.add_parent(parameter, parent); + } + + if let Some(body) = body { + self.add_parent(body, parent); + } + } + + Variable { initializer, .. } => { + if let Some(initializer) = initializer { + self.add_parent(initializer, parent); + } + } + + Enum { + ref variants, + integral_type, + .. + } => { + if integral_type.is_some() { + for &variant in variants { + self.add_parent(variant, parent); + } + } + } + + EnumConstant { .. } => {} + + Typedef { .. } => {} + + Struct { ref fields, .. } => { + if let Some(fields) = fields { + for &field in fields { + self.add_parent(field, parent); + } + } + } + + Union { ref fields, .. } => { + if let Some(fields) = fields { + for &field in fields { + self.add_parent(field, parent); + } + } + } + + Field { .. } => {} + + MacroObject { .. } => {} + + MacroFunction { .. } => {} + + NonCanonicalDecl { .. } => {} + + StaticAssert { assert_expr, .. } => { + self.add_parent(assert_expr, parent); + } + } + } + + /// Construct a map from top-level decls in the main file to their source ranges. + pub fn top_decl_locs(&self) -> IndexMap { + let mut name_loc_map = IndexMap::new(); + let mut prev_end_loc = SrcLoc { + fileid: 0, + line: 0, + column: 0, + }; + // Sort decls by source location so we can reason about the possibly comment-containing gaps + // between them. + let mut decls_sorted = self.c_decls_top.clone(); + // Break ties in `begin_loc` (e.g. from `int a, b;`) using `end_loc`. + decls_sorted + .sort_by_key(|decl| (self.c_decls[decl].begin_loc(), self.c_decls[decl].end_loc())); + for decl_id in &decls_sorted { + let decl = &self.c_decls[decl_id]; + let begin_loc: SrcLoc = decl.begin_loc().expect("no begin loc for top-level decl"); + let end_loc: SrcLoc = decl.end_loc().expect("no end loc for top-level decl"); + + // Skip fileid 0; this is not a real file, so these source locations aren't important. + if begin_loc.fileid == 0 { + continue; + } + if begin_loc == end_loc { + log::warn!( + "zero-length source range for top-level decl; skipping. source ranges for \ + top-level decls may be incorrect.\ndecl: {decl:?}" + ); + continue; + } + + // If encountering a new file, reset end of last top-level decl. + if prev_end_loc.fileid != begin_loc.fileid { + prev_end_loc = SrcLoc { + fileid: begin_loc.fileid, + line: 1, + column: 1, + } + } + + // This definition ends before the previous one does, i.e. it is nested. + // This does not generally occur for regular definitions, e.g. variables within + // functions, because the variables will not be top-level decls. But it can occur + // for macros defined inside functions, since all macros are top-level decls! + let is_nested = end_loc < prev_end_loc; + + // Clang emits declarations of builtins (and opaque types such as when encountering + // `struct undeclared *`) at their first usage site. This means that they are usually + // nested within another function, and (at least with how the C AST is currently + // exported) they have an end location with line and column zero. Fix this up before + // continuing to maintain the invariant that begin is not ordered after end. + if is_nested + && end_loc.line == 0 + && end_loc.column == 0 + && !(begin_loc.line == 0 && begin_loc.column == 0) + { + log::debug!("skipping nested decl with zero end line/col: {decl:?}"); + continue; + } + + // If the beginning is not ordered after the end, skip this decl and warn. + if begin_loc > end_loc { + log::warn!( + "backward source range for top-level decl; skipping. source ranges for \ + top-level decls may be incorrect.\ndecl: {decl:?}" + ); + continue; + } + + // End of the previous decl is the start of comments pertaining to the current one. + let earliest_begin_loc = if is_nested { begin_loc } else { prev_end_loc }; + + // Include only decls from the main file. + if self.c_decls_top.contains(decl_id) + && self.get_source_path(decl) == Some(&self.main_file) + { + // For multiple decls, e.g. `int a, b;`, `begin_loc` is shared, in which case it is + // earlier than `earliest_begin_loc` for decls after the first; to maintain their + // relative order we must either move `earliest_begin_loc` earlier or move + // `begin_loc` later. + // For now, we move `begin_loc` later, so that the range used by each variable from + // a multiple decl does not overlap the others. If other tooling would benefit more + // from maximal but overlapping ranges, we could go the other way. + let begin_loc = begin_loc.max(earliest_begin_loc); + let entry = CDeclSrcRange { + earliest_begin: earliest_begin_loc, + strict_begin: begin_loc, + end: end_loc, + }; + name_loc_map.insert(*decl_id, entry); + } + if !is_nested { + prev_end_loc = end_loc; + } + } + name_loc_map + } + + pub fn iter_decls(&self) -> indexmap::map::Iter<'_, CDeclId, CDecl> { + self.c_decls.iter() + } + + pub fn iter_mut_decls(&mut self) -> indexmap::map::IterMut<'_, CDeclId, CDecl> { + self.c_decls.iter_mut() + } + + pub fn get_decl(&self, key: &CDeclId) -> Option<&CDecl> { + self.c_decls.get(key) + } + + /// Return the list of types for a list of declared function parameters. + /// + /// Returns `None` if one of the parameters is not a `CDeclKind::Variable`, e.g. if it was not a + /// function parameter but actually some other kind of declaration. + pub fn tys_of_params(&self, parameters: &[CDeclId]) -> Option> { + parameters + .iter() + .map(|p| match self.index(*p).kind { + CDeclKind::Variable { typ, .. } => Some(CQualTypeId::new(typ.ctype)), + _ => None, + }) + .collect() + } + + /// Return the most precise possible CTypeKind for the given function declaration. + /// Specifically, ensures that arguments' types are not resolved to underlying types if they were + /// declared as typedefs, but returned as those typedefs. + /// + /// The passed CDeclId must refer to a function declaration. + pub fn fn_decl_ty_with_declared_args(&self, func_decl: &CDeclKind) -> CTypeKind { + if let CDeclKind::Function { + typ, parameters, .. + } = func_decl + { + let typ = self.resolve_type_id(*typ); + let decl_arg_tys = self.tys_of_params(parameters).unwrap(); + let typ_kind = &self[typ].kind; + if let &CTypeKind::Function(ret, ref _arg_tys, a, b, c) = typ_kind { + return CTypeKind::Function(ret, decl_arg_tys, a, b, c); + } + panic!("expected {typ:?} to be CTypeKind::Function, but it was {typ_kind:?}") + } + panic!("expected a CDeclKind::Function, but passed {func_decl:?}") + } + + pub fn has_inner_struct_decl(&self, decl_id: CDeclId) -> bool { + matches!( + self.index(decl_id).kind, + CDeclKind::Struct { + manual_alignment: Some(_), + .. + } + ) + } + + pub fn is_packed_struct_decl(&self, decl_id: CDeclId) -> bool { + use CDeclKind::*; + matches!( + self.index(decl_id).kind, + Struct { + is_packed: true, + .. + } | Struct { + max_field_alignment: Some(_), + .. + } + ) + } +} + +impl Index for TypedAstContext { + type Output = CDecl; + + fn index(&self, index: CDeclId) -> &CDecl { + match self.c_decls.get(&index) { + None => panic!("Could not find {:?} in TypedAstContext", index), + Some(ty) => ty, + } + } +} diff --git a/c2rust-transpile/src/c_ast/c_expr.rs b/c2rust-transpile/src/c_ast/c_expr.rs new file mode 100644 index 0000000000..c07c7a8020 --- /dev/null +++ b/c2rust-transpile/src/c_ast/c_expr.rs @@ -0,0 +1,977 @@ +use crate::c_ast::c_decl::{CDeclId, CDeclKind, CFieldId}; +use crate::c_ast::c_stmt::CStmtId; +use crate::c_ast::c_type::{CQualTypeId, CTypeId, CTypeKind}; +use crate::c_ast::{Located, SomeId, TypedAstContext}; +use c2rust_ast_exporter::clang_ast::LRValue; +use std::fmt::{self, Debug, Display}; +use std::ops::Index; + +#[derive(Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Copy, Clone)] +pub struct CExprId(pub u64); + +/// Represents an expression in C (6.5 Expressions) +/// +/// This is modeled on Clang's APIs, so where documentation +/// is lacking here, look at Clang. +/// +/// We've kept a qualified type on every node since Clang has this information available, and since +/// the semantics of translations of certain constructs often depend on the type of the things they +/// are given. +/// +/// As per the C standard, qualifiers on types make sense only on lvalues. +#[derive(Debug, Clone)] +pub enum CExprKind { + /// Literal. + Literal(CQualTypeId, CLiteral), + + /// Unary operator. + Unary(CQualTypeId, CUnOp, CExprId, LRValue), + + /// Unary type operator. + UnaryType(CQualTypeId, CUnTypeOp, Option, CQualTypeId), + + /// `offsetof` expression. + OffsetOf(CQualTypeId, OffsetOfKind), + + /// Binary operator. + Binary( + CQualTypeId, + CBinOp, + CExprId, + CExprId, + Option, + Option, + ), + + /// Implicit cast. + ImplicitCast(CQualTypeId, CExprId, CastKind, Option, LRValue), + + /// Explicit cast. + ExplicitCast(CQualTypeId, CExprId, CastKind, Option, LRValue), + + /// Constant context expression. + ConstantExpr(CQualTypeId, CExprId, Option), + + /// Reference to a decl (a variable, for instance). + // TODO: consider enforcing what types of declarations are allowed here + DeclRef(CQualTypeId, CDeclId, LRValue), + + /// Function call. + Call(CQualTypeId, CExprId, Vec), + + /// Member access. + Member(CQualTypeId, CExprId, CDeclId, MemberKind, LRValue), + + /// Array subscript access. + ArraySubscript(CQualTypeId, CExprId, CExprId, LRValue), + + /// Ternary conditional operator. + Conditional(CQualTypeId, CExprId, CExprId, CExprId), + + /// Binary conditional operator `?:` (GNU extension). + BinaryConditional(CQualTypeId, CExprId, CExprId), + + /// Initializer list. + /// + /// * type + /// * initializers + /// * union field + /// * syntactic form + InitList(CQualTypeId, Vec, Option, Option), + + /// Designated initializer. + ImplicitValueInit(CQualTypeId), + + /// Parenthesized expression. + /// + /// Ignored, but needed so we have a corresponding node. + Paren(CQualTypeId, CExprId), + + /// Compound literal. + CompoundLiteral(CQualTypeId, CExprId), + + /// Predefined expression. + Predefined(CQualTypeId, CExprId), + + /// Statement expression. + Statements(CQualTypeId, CStmtId), + + /// Variable argument list. + VAArg(CQualTypeId, CExprId), + + /// Unsupported shuffle vector operation. + ShuffleVector(CQualTypeId, Vec), + + /// Unsupported convert vector operation. + ConvertVector(CQualTypeId, Vec), + + /// From syntactic form of initializer list expressions. + DesignatedInitExpr(CQualTypeId, Vec, CExprId), + + /// GNU choose expression. + /// + /// * condition + /// * true expr + /// * false expr + /// * was condition true? + Choose(CQualTypeId, CExprId, CExprId, CExprId, bool), + + /// GNU/C11 atomic expression. + Atomic { + typ: CQualTypeId, + name: String, + ptr: CExprId, + order: CExprId, + val1: Option, + order_fail: Option, + val2: Option, + weak: Option, + }, + + BadExpr, +} + +pub type CExpr = Located; + +impl CExprKind { + pub fn lrvalue(&self) -> LRValue { + match *self { + CExprKind::Unary(_, _, _, lrvalue) + | CExprKind::DeclRef(_, _, lrvalue) + | CExprKind::ImplicitCast(_, _, _, _, lrvalue) + | CExprKind::ExplicitCast(_, _, _, _, lrvalue) + | CExprKind::Member(_, _, _, _, lrvalue) + | CExprKind::ArraySubscript(_, _, _, lrvalue) => lrvalue, + _ => LRValue::RValue, + } + } + + pub fn get_qual_type(&self) -> Option { + self.clone().get_qual_type_mut().copied() + } + + pub fn get_qual_type_mut(&mut self) -> Option<&mut CQualTypeId> { + match self { + CExprKind::BadExpr => None, + CExprKind::Literal(ty, _) + | CExprKind::OffsetOf(ty, _) + | CExprKind::Unary(ty, _, _, _) + | CExprKind::UnaryType(ty, _, _, _) + | CExprKind::Binary(ty, _, _, _, _, _) + | CExprKind::ImplicitCast(ty, _, _, _, _) + | CExprKind::ExplicitCast(ty, _, _, _, _) + | CExprKind::DeclRef(ty, _, _) + | CExprKind::Call(ty, _, _) + | CExprKind::Member(ty, _, _, _, _) + | CExprKind::ArraySubscript(ty, _, _, _) + | CExprKind::Conditional(ty, _, _, _) + | CExprKind::BinaryConditional(ty, _, _) + | CExprKind::InitList(ty, _, _, _) + | CExprKind::ImplicitValueInit(ty) + | CExprKind::Paren(ty, _) + | CExprKind::CompoundLiteral(ty, _) + | CExprKind::Predefined(ty, _) + | CExprKind::Statements(ty, _) + | CExprKind::VAArg(ty, _) + | CExprKind::ShuffleVector(ty, _) + | CExprKind::ConvertVector(ty, _) + | CExprKind::DesignatedInitExpr(ty, _, _) + | CExprKind::ConstantExpr(ty, _, _) => Some(ty), + CExprKind::Choose(ty, _, _, _, _) | CExprKind::Atomic { typ: ty, .. } => Some(ty), + } + } + + pub fn get_type(&self) -> Option { + self.get_qual_type().map(|x| x.ctype) + } + + /// Try to determine the truthiness or falsiness of the expression. Return `None` if we can't + /// say anything. + pub fn get_bool(&self) -> Option { + match *self { + CExprKind::Literal(_, ref lit) => Some(lit.get_bool()), + _ => None, + } + } +} + +/// An OffsetOf Expr may or may not be a constant +#[derive(Debug, Clone)] +pub enum OffsetOfKind { + /// An Integer Constant Expr + Constant(u64), + /// Contains more information to generate + /// an offset_of! macro invocation + /// Struct Type, Field Decl Id, Index Expr + Variable(CQualTypeId, CDeclId, CExprId), +} + +#[derive(Copy, Debug, Clone)] +pub enum MemberKind { + Arrow, + Dot, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum CastKind { + BitCast, + LValueToRValue, + NoOp, + ToUnion, + ArrayToPointerDecay, + FunctionToPointerDecay, + NullToPointer, + IntegralToPointer, + PointerToIntegral, + ToVoid, + IntegralCast, + IntegralToBoolean, + IntegralToFloating, + FloatingToIntegral, + FloatingToBoolean, + BooleanToSignedIntegral, + PointerToBoolean, + FloatingCast, + FloatingRealToComplex, + FloatingComplexToReal, + FloatingComplexCast, + FloatingComplexToIntegralComplex, + IntegralRealToComplex, + IntegralComplexToReal, + IntegralComplexToBoolean, + IntegralComplexCast, + IntegralComplexToFloatingComplex, + BuiltinFnToFnPtr, + ConstCast, + VectorSplat, + AtomicToNonAtomic, + NonAtomicToAtomic, +} + +/// Represents a unary operator in C (6.5.3 Unary operators) and GNU C extensions +#[derive(Debug, Clone, Copy)] +pub enum CUnOp { + AddressOf, // &x + Deref, // *x + Plus, // +x + PostIncrement, // x++ + PreIncrement, // ++x + Negate, // -x + PostDecrement, // x-- + PreDecrement, // --x + Complement, // ~x + Not, // !x + Real, // [GNU C] __real x + Imag, // [GNU C] __imag x + Extension, // [GNU C] __extension__ x + Coawait, // [C++ Coroutines] co_await x +} + +impl CUnOp { + pub fn as_str(&self) -> &'static str { + use CUnOp::*; + match self { + AddressOf => "&", + Deref => "*", + Plus => "+", + PreIncrement => "++", + PostIncrement => "++", + Negate => "-", + PreDecrement => "--", + PostDecrement => "--", + Complement => "~", + Not => "!", + Real => "__real", + Imag => "__imag", + Extension => "__extension__", + Coawait => "co_await", + } + } + + /// Obtain the expected type of a unary expression based on the operator and its argument type + pub fn expected_result_type( + &self, + ast_context: &TypedAstContext, + arg_type: CQualTypeId, + ) -> Option { + use CUnOp::*; + let resolved_ty = ast_context.resolve_type(arg_type.ctype); + Some(match self { + // We could construct CTypeKind::Pointer here, but it is not guaranteed to have a + // corresponding `CTypeId` in the `TypedAstContext`, so bail out instead + AddressOf => return None, + Deref => { + if let CTypeKind::Pointer(inner) = resolved_ty.kind { + inner + } else { + panic!("dereferencing non-pointer type!") + } + } + Not => { + return ast_context + .type_for_kind(&CTypeKind::Int) + .map(CQualTypeId::new) + } + Real | Imag => { + if let CTypeKind::Complex(inner) = resolved_ty.kind { + CQualTypeId::new(inner) + } else { + panic!("__real or __imag applied to non-complex type!") + } + } + Coawait => panic!("trying to propagate co_await type"), + _ => CQualTypeId::new(arg_type.ctype), + }) + } +} + +impl Display for CUnOp { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.as_str()) + } +} + +/// Represents a unary type operator in C +#[derive(Debug, Clone, Copy)] +pub enum CUnTypeOp { + SizeOf, + AlignOf, + PreferredAlignOf, +} + +impl CUnTypeOp { + pub fn as_str(&self) -> &'static str { + use CUnTypeOp::*; + match self { + SizeOf => "sizeof", + AlignOf => "alignof", + PreferredAlignOf => "__alignof", + } + } +} + +impl Display for CUnTypeOp { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.as_str()) + } +} + +impl CUnOp { + /// Check if the operator is rendered before or after its operand. + pub fn is_prefix(&self) -> bool { + !matches!(*self, CUnOp::PostIncrement | CUnOp::PostDecrement) + } +} + +/// Represents a binary operator in C (6.5.5 Multiplicative operators - 6.5.14 Logical OR operator) +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum CBinOp { + Multiply, // * + Divide, // / + Modulus, // % + Add, // + + Subtract, // - + ShiftLeft, // << + ShiftRight, // >> + Less, // < + Greater, // > + LessEqual, // <= + GreaterEqual, // >= + EqualEqual, // == + NotEqual, // != + BitAnd, // & + BitXor, // ^ + BitOr, // | + And, // && + Or, // || + + AssignAdd, // += + AssignSubtract, // -= + AssignMultiply, // *= + AssignDivide, // /= + AssignModulus, // %= + AssignBitXor, // ^= + AssignShiftLeft, // <<= + AssignShiftRight, // >>= + AssignBitOr, // |= + AssignBitAnd, // &= + + Assign, // = + Comma, // , +} + +impl CBinOp { + pub fn as_str(&self) -> &'static str { + use CBinOp::*; + match self { + Multiply => "*", + Divide => "/", + Modulus => "%", + Add => "+", + Subtract => "-", + ShiftLeft => "<<", + ShiftRight => ">>", + Less => "<", + Greater => ">", + LessEqual => "<=", + GreaterEqual => ">=", + EqualEqual => "==", + NotEqual => "!=", + BitAnd => "&", + BitXor => "^", + BitOr => "|", + And => "&&", + Or => "||", + + AssignAdd => "+=", + AssignSubtract => "-=", + AssignMultiply => "*=", + AssignDivide => "/=", + AssignModulus => "%=", + AssignBitXor => "^=", + AssignShiftLeft => "<<=", + AssignShiftRight => ">>=", + AssignBitOr => "|=", + AssignBitAnd => "&=", + + Assign => "=", + Comma => ", ", + } + } + + /// Does the rust equivalent of this operator have type (T, T) -> U? + #[rustfmt::skip] + pub fn input_types_same(&self) -> bool { + use CBinOp::*; + self.all_types_same() || matches!(self, + Less | Greater | LessEqual | GreaterEqual | EqualEqual | NotEqual + | And | Or + | AssignAdd | AssignSubtract | AssignMultiply | AssignDivide | AssignModulus + | AssignBitXor | AssignShiftLeft | AssignShiftRight | AssignBitOr | AssignBitAnd + | Assign + ) + } + + /// Does the rust equivalent of this operator have type (T, T) -> T? + /// This ignores cases where one argument is a pointer and we translate to `.offset()`. + pub fn all_types_same(&self) -> bool { + use CBinOp::*; + matches!( + self, + Multiply | Divide | Modulus | Add | Subtract | BitAnd | BitXor | BitOr + ) + } +} + +impl Display for CBinOp { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.as_str()) + } +} + +impl CBinOp { + /// Maps compound assignment operators to operator underlying them, and returns `None` for all + /// other operators. + /// + /// For example, `AssignAdd` maps to `Some(Add)` but `Add` maps to `None`. + pub fn underlying_assignment(&self) -> Option { + use CBinOp::*; + Some(match *self { + AssignAdd => Add, + AssignSubtract => Subtract, + AssignMultiply => Multiply, + AssignDivide => Divide, + AssignModulus => Modulus, + AssignBitXor => BitXor, + AssignShiftLeft => ShiftLeft, + AssignShiftRight => ShiftRight, + AssignBitOr => BitOr, + AssignBitAnd => BitAnd, + _ => return None, + }) + } + + /// Determines whether or not this is an assignment op + pub fn is_assignment(&self) -> bool { + matches!(self, Self::Assign) || self.underlying_assignment().is_some() + } +} + +#[derive(Eq, PartialEq, Debug, Copy, Clone)] +pub enum IntBase { + Dec, + Hex, + Oct, +} + +#[derive(Debug, Clone)] +pub enum CLiteral { + Integer(u64, IntBase), // value and base + Character(u64), + Floating(f64, String), + String(Vec, u8), // Literal bytes and unit byte width +} + +impl CLiteral { + /// Determine the truthiness or falsiness of the literal. + pub fn get_bool(&self) -> bool { + use CLiteral::*; + match *self { + Integer(x, _) => x != 0u64, + Character(x) => x != 0u64, + Floating(x, _) => x != 0f64, + _ => true, + } + } +} + +/// Represents a constant integer expression as used in a case expression +#[derive(Debug, Clone, Copy, Eq, PartialEq)] +pub enum ConstIntExpr { + U(u64), + I(i64), +} + +#[derive(Copy, Clone, Debug)] +pub enum Designator { + Index(u64), + Range(u64, u64), + Field(CFieldId), +} + +impl TypedAstContext { + pub(super) fn add_expr_parents(&mut self, id: CExprId, kind: &CExprKind) { + use CExprKind::*; + let parent = SomeId::Expr(id); + + match *kind { + Literal(..) => {} + + Unary(_, _, expr, _) => { + self.add_parent(expr, parent); + } + + UnaryType(_, _, expr, _) => { + if let Some(expr) = expr { + self.add_parent(expr, parent); + } + } + + OffsetOf(_, ref kind) => match *kind { + OffsetOfKind::Constant(..) => {} + OffsetOfKind::Variable(_, _, expr) => { + self.add_parent(expr, parent); + } + }, + + Binary(_, _, lhs, rhs, _, _) => { + self.add_parent(lhs, parent); + self.add_parent(rhs, parent); + } + + ImplicitCast(_, expr, _, _, _) => { + self.add_parent(expr, parent); + } + + ExplicitCast(_, expr, _, _, _) => { + self.add_parent(expr, parent); + } + + ConstantExpr(_, expr, _) => { + self.add_parent(expr, parent); + } + + DeclRef(..) => {} + + Call(_, func, ref args) => { + self.add_parent(func, parent); + + for &arg in args { + self.add_parent(arg, parent); + } + } + + Member(_, expr, _, _, _) => { + self.add_parent(expr, parent); + } + + ArraySubscript(_, lhs, rhs, _) => { + self.add_parent(lhs, parent); + self.add_parent(rhs, parent); + } + + Conditional(_, cond, lhs, rhs) => { + self.add_parent(cond, parent); + self.add_parent(lhs, parent); + self.add_parent(rhs, parent); + } + + BinaryConditional(_, cond, rhs) => { + self.add_parent(cond, parent); + self.add_parent(rhs, parent); + } + + InitList(_, ref exprs, _, syntactic_form) => { + for &expr in exprs { + self.add_parent(expr, parent); + } + + if let Some(syntactic_form) = syntactic_form { + self.add_parent(syntactic_form, parent); + } + } + + ImplicitValueInit(..) => {} + + Paren(_, expr) => { + self.add_parent(expr, parent); + } + + CompoundLiteral(_, expr) => { + self.add_parent(expr, parent); + } + + Predefined(_, expr) => { + self.add_parent(expr, parent); + } + + Statements(_, stmt) => { + self.add_parent(stmt, parent); + } + + VAArg(_, expr) => { + self.add_parent(expr, parent); + } + + ShuffleVector(_, ref exprs) => { + for &expr in exprs { + self.add_parent(expr, parent); + } + } + + ConvertVector(_, ref exprs) => { + for &expr in exprs { + self.add_parent(expr, parent); + } + } + + DesignatedInitExpr(_, _, expr) => { + self.add_parent(expr, parent); + } + + Choose(_, cond, lhs, rhs, _) => { + self.add_parent(cond, parent); + self.add_parent(lhs, parent); + self.add_parent(rhs, parent); + } + + Atomic { + ptr, + order, + val1, + order_fail, + val2, + weak, + .. + } => { + self.add_parent(ptr, parent); + self.add_parent(order, parent); + + if let Some(val1) = val1 { + self.add_parent(val1, parent); + } + + if let Some(order_fail) = order_fail { + self.add_parent(order_fail, parent); + } + + if let Some(val2) = val2 { + self.add_parent(val2, parent); + } + + if let Some(weak) = weak { + self.add_parent(weak, parent); + } + } + + BadExpr => {} + } + } + + pub fn is_null_expr(&self, expr_id: CExprId) -> bool { + use CExprKind::*; + match self[expr_id].kind { + ExplicitCast(_, _, CastKind::NullToPointer, _, _) + | ImplicitCast(_, _, CastKind::NullToPointer, _, _) => true, + + ExplicitCast(ty, e, CastKind::BitCast, _, _) + | ImplicitCast(ty, e, CastKind::BitCast, _, _) => { + self.resolve_type(ty.ctype).kind.is_pointer() && self.is_null_expr(e) + } + + _ => false, + } + } + + /// Returns the expression inside any number of nested parentheses. + pub fn resolve_parens(&self, mut expr_id: CExprId) -> CExprId { + while let CExprKind::Paren(_, subexpr) = self.index(expr_id).kind { + expr_id = subexpr; + } + + expr_id + } + + /// Returns the expression inside an `__extension__` operator. + pub fn resolve_extension(&self, expr_id: CExprId) -> CExprId { + if let CExprKind::Unary(_, CUnOp::Extension, subexpr, _) = self.index(expr_id).kind { + subexpr + } else { + expr_id + } + } + + /// Unwraps a predefined expression, if there is one. + pub fn unwrap_predefined_ident(&self, mut expr_id: CExprId) -> CExprId { + expr_id = self.resolve_extension(self.resolve_parens(expr_id)); + + if let CExprKind::Predefined(_, subexpr) = self.index(expr_id).kind { + subexpr + } else { + expr_id + } + } + + /// Unwraps the underlying expression beneath any casts. + pub fn unwrap_cast_expr(&self, mut expr_id: CExprId) -> CExprId { + while let CExprKind::Paren(_, subexpr) + | CExprKind::ImplicitCast(_, subexpr, _, _, _) + | CExprKind::ExplicitCast(_, subexpr, _, _, _) = self.index(expr_id).kind + { + expr_id = subexpr; + } + + expr_id + } + + /// Unwraps the underlying expression beneath any implicit casts. + pub fn unwrap_implicit_cast_expr(&self, mut expr_id: CExprId) -> CExprId { + while let CExprKind::ImplicitCast(_, subexpr, _, _, _) = self.index(expr_id).kind { + expr_id = subexpr; + } + + expr_id + } + + /// Resolve true expression type, iterating through any casts and variable + /// references. + pub fn resolve_expr_type_id(&self, expr_id: CExprId) -> Option<(CExprId, CTypeId)> { + let expr = &self.index(expr_id).kind; + let mut ty = expr.get_type(); + use CExprKind::*; + match expr { + ImplicitCast(_, subexpr, _, _, _) + | ExplicitCast(_, subexpr, _, _, _) + | Paren(_, subexpr) => { + return self.resolve_expr_type_id(*subexpr); + } + DeclRef(_, decl_id, _) => { + let decl = self.index(*decl_id); + use CDeclKind::*; + match decl.kind { + Function { typ, .. } => { + ty = Some(self.resolve_type_id(typ)); + } + Variable { typ, .. } | Typedef { typ, .. } => { + ty = Some(self.resolve_type_id(typ.ctype)); + } + _ => {} + } + } + _ => {} + } + ty.map(|ty| (expr_id, ty)) + } + + /// Extract decl of referenced function. + /// Looks for ImplicitCast(FunctionToPointerDecay, DeclRef(function_decl)) + pub fn fn_declref_decl(&self, func_expr: CExprId) -> Option<&CDeclKind> { + use CastKind::FunctionToPointerDecay; + if let CExprKind::ImplicitCast(_, fexp, FunctionToPointerDecay, _, _) = self[func_expr].kind + { + if let CExprKind::DeclRef(_ty, decl_id, _rv) = &self[fexp].kind { + let decl = &self.index(*decl_id).kind; + assert!(matches!(decl, CDeclKind::Function { .. })); + return Some(decl); + } + } + None + } + + /// Return the id of the most precise possible type for the function referenced by the given + /// expression, if any. + pub fn fn_declref_ty_with_declared_args(&self, func_expr: CExprId) -> Option { + if let Some(func_decl @ CDeclKind::Function { .. }) = self.fn_declref_decl(func_expr) { + let kind_with_declared_args = self.fn_decl_ty_with_declared_args(func_decl); + let specific_typ = self + .type_for_kind(&kind_with_declared_args) + .unwrap_or_else(|| panic!("no type for kind {kind_with_declared_args:?}")); + return Some(CQualTypeId::new(specific_typ)); + } + None + } + + /// Pessimistically try to check if an expression has side effects. If it does, or we can't tell + /// that it doesn't, return `false`. + pub fn is_expr_pure(&self, expr: CExprId) -> bool { + use CExprKind::*; + let pure = |expr| self.is_expr_pure(expr); + match self.index(expr).kind { + BadExpr | + ShuffleVector(..) | + ConvertVector(..) | + Call(..) | + Unary(_, CUnOp::PreIncrement, _, _) | + Unary(_, CUnOp::PostIncrement, _, _) | + Unary(_, CUnOp::PreDecrement, _, _) | + Unary(_, CUnOp::PostDecrement, _, _) | + Binary(_, CBinOp::Assign, _, _, _, _) | + InitList { .. } | + ImplicitValueInit { .. } | + Predefined(..) | + Statements(..) | // TODO: more precision + VAArg(..) | + Atomic{..} => false, + + Literal(_, _) | + DeclRef(_, _, _) | + UnaryType(_, _, _, _) | + OffsetOf(..) | + ConstantExpr(..) => true, + + DesignatedInitExpr(_,_,e) | + ImplicitCast(_, e, _, _, _) | + ExplicitCast(_, e, _, _, _) | + Member(_, e, _, _, _) | + Paren(_, e) | + CompoundLiteral(_, e) | + Unary(_, _, e, _) => pure(e), + + Binary(_, op, _, _, _, _) if op.underlying_assignment().is_some() => false, + Binary(_, _, lhs, rhs, _, _) => pure(lhs) && pure(rhs), + + ArraySubscript(_, lhs, rhs, _) => pure(lhs) && pure(rhs), + Conditional(_, c, lhs, rhs) => pure(c) && pure(lhs) && pure(rhs), + BinaryConditional(_, c, rhs) => pure(c) && pure(rhs), + Choose(_, c, lhs, rhs, _) => pure(c) && pure(lhs) && pure(rhs), + } + } + + /// Pessimistically try to check if an expression doesn't return. + /// If it does, or we can't tell that it doesn't, return `false`. + pub fn expr_diverges(&self, expr_id: CExprId) -> bool { + let func_id = match self.index(expr_id).kind { + CExprKind::Call(_, func_id, _) => func_id, + _ => return false, + }; + + let type_id = match self[func_id].kind.get_type() { + None => return false, + Some(t) => t, + }; + let pointed_id = match self.index(type_id).kind { + CTypeKind::Pointer(pointer_qualtype) => pointer_qualtype.ctype, + _ => return false, + }; + + match self.index(pointed_id).kind { + CTypeKind::Function(_, _, _, no_return, _) => no_return, + _ => false, + } + } + + /// Pessimistically try to check if an expression is `const`. + /// If it's not, or we can't tell if it is, return `false`. + /// + /// This should be a top-down, pessimistic/conservative analysis. + pub fn is_const_expr(&self, expr: CExprId) -> bool { + let is_const = |expr| self.is_const_expr(expr); + + use CExprKind::*; + match self[expr].kind { + // A literal is always `const`. + Literal(_, _) => true, + // Unary ops should be `const`. + // TODO handle `f128` or use the primitive type. + Unary(_, _, expr, _) => is_const(expr), + // Not sure what a `None` `CExprId` means here + // or how to detect a `sizeof` of a VLA, which is non-`const`, + // although it seems we don't handle `sizeof(VLAs)` + // correctly in macros elsewhere already. + UnaryType(_, _, expr, _) => expr.map_or(true, is_const), + // Not sure what a `OffsetOfKind::Variable` means. + OffsetOf(_, _) => true, + // `ptr::offset` (ptr `CBinOp::Add`) was `const` stabilized in `1.61.0`. + // `ptr::offset_from` (ptr `CBinOp::Subtract`) was `const` stabilized in `1.65.0`. + // TODO `f128` is not yet handled, as we should eventually + // switch to the (currently unstable) `f128` primitive type (#1262). + Binary(_, _, lhs, rhs, _, _) => is_const(lhs) && is_const(rhs), + // `as` casts are always `const`. + ImplicitCast(_, expr, _, _, _) => is_const(expr), + // `as` casts are always `const`. + // TODO This is `const`, although there's a bug #853. + ExplicitCast(_, expr, _, _, _) => is_const(expr), + // This is used in `const` locations like `match` patterns and array lengths, so it must be `const`. + ConstantExpr(_, _, _) => true, + // A reference in an already otherwise `const` context should be `const` itself. + DeclRef(_, _, _) => true, + Call(_, fn_expr, ref args) => { + let is_const_fn = false; // TODO detect which `fn`s are `const`. + is_const(fn_expr) && args.iter().copied().all(is_const) && is_const_fn + } + Member(_, expr, _, _, _) => is_const(expr), + ArraySubscript(_, array, index, _) => is_const(array) && is_const(index), + Conditional(_, cond, if_true, if_false) => { + is_const(cond) && is_const(if_true) && is_const(if_false) + } + BinaryConditional(_, cond, if_false) => is_const(cond) && is_const(if_false), + InitList(_, ref inits, _, _) => inits.iter().copied().all(is_const), + ImplicitValueInit(_) => true, + Paren(_, expr) => is_const(expr), + CompoundLiteral(_, expr) => is_const(expr), + Predefined(_, expr) => is_const(expr), + Statements(_, stmt) => self.is_const_stmt(stmt), + VAArg(_, expr) => is_const(expr), + // SIMD is not yet `const` in Rust. + ShuffleVector(_, _) | ConvertVector(_, _) => false, + DesignatedInitExpr(_, _, expr) => is_const(expr), + Choose(_, cond, if_true, if_false, _) => { + is_const(cond) && is_const(if_true) && is_const(if_false) + } + // Atomics are not yet `const` in Rust. + Atomic { .. } => false, + BadExpr => false, + } + } +} + +impl Index for TypedAstContext { + type Output = CExpr; + fn index(&self, index: CExprId) -> &CExpr { + static BADEXPR: CExpr = Located { + loc: None, + kind: CExprKind::BadExpr, + }; + match self.c_exprs.get(&index) { + None => &BADEXPR, // panic!("Could not find {:?} in TypedAstContext", index), + Some(e) => { + // Transparently index through Paren expressions + if let CExprKind::Paren(_, subexpr) = e.kind { + self.index(subexpr) + } else { + e + } + } + } + } +} diff --git a/c2rust-transpile/src/c_ast/c_stmt.rs b/c2rust-transpile/src/c_ast/c_stmt.rs new file mode 100644 index 0000000000..c7e5a368ba --- /dev/null +++ b/c2rust-transpile/src/c_ast/c_stmt.rs @@ -0,0 +1,267 @@ +use crate::c_ast::c_decl::CDeclId; +use crate::c_ast::c_expr::{CExprId, ConstIntExpr}; +use crate::c_ast::{Attribute, Located, SomeId, TypedAstContext}; +use std::fmt::Debug; +use std::ops::Index; + +#[derive(Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Copy, Clone)] +pub struct CStmtId(pub u64); + +// These are references into particular variants of AST nodes +pub type CLabelId = CStmtId; // Labels point into the 'StmtKind::Label' that declared the label + +/// Represents a statement in C (6.8 Statements) +/// +/// Reflects the types in +#[derive(Debug, Clone)] +pub enum CStmtKind { + // Labeled statements (6.8.1) + // + // All of these have a `CStmtId` to represent the substatement that comes after them + Label(CStmtId), + Case(CExprId, CStmtId, ConstIntExpr), + Default(CStmtId), + + // Compound statements (6.8.2) + Compound(Vec), + + // Expression and null statements (6.8.3) + Expr(CExprId), + Empty, + + // Selection statements (6.8.4) + If { + scrutinee: CExprId, + true_variant: CStmtId, + false_variant: Option, + }, + Switch { + scrutinee: CExprId, + body: CStmtId, + }, + + // Iteration statements (6.8.5) + While { + condition: CExprId, + body: CStmtId, + }, + DoWhile { + body: CStmtId, + condition: CExprId, + }, + ForLoop { + init: Option, + condition: Option, + increment: Option, + body: CStmtId, + }, + + // Jump statements (6.8.6) + Goto(CLabelId), + Break, + Continue, + Return(Option), + + // Declarations (variables, etc.) + Decls(Vec), + + // GCC inline assembly + Asm { + asm: String, + inputs: Vec, + outputs: Vec, + clobbers: Vec, + is_volatile: bool, + }, + + // Statements annotated with attributes. The substatement can be a NULL + // statement in case of __attribute__((__fallthrough__)) at the end of a + // case statement + Attributed { + attributes: Vec, + substatement: CStmtId, + }, +} + +pub type CStmt = Located; + +#[derive(Clone, Debug)] +pub struct AsmOperand { + pub constraints: String, + pub expression: CExprId, +} + +impl TypedAstContext { + pub(super) fn add_stmt_parents(&mut self, id: CStmtId, kind: &CStmtKind) { + use CStmtKind::*; + let parent = SomeId::Stmt(id); + + match *kind { + Label(stmt) => { + self.add_parent(stmt, parent); + } + + Case(expr, stmt, _) => { + self.add_parent(expr, parent); + self.add_parent(stmt, parent); + } + + Default(stmt) => { + self.add_parent(stmt, parent); + } + + Compound(ref stmts) => { + for &stmt in stmts { + self.add_parent(stmt, parent); + } + } + + Expr(expr) => { + self.add_parent(expr, parent); + } + + Empty => {} + + If { + scrutinee, + true_variant, + false_variant, + } => { + self.add_parent(scrutinee, parent); + self.add_parent(true_variant, parent); + + if let Some(false_variant) = false_variant { + self.add_parent(false_variant, parent); + } + } + Switch { scrutinee, body } => { + self.add_parent(scrutinee, parent); + self.add_parent(body, parent); + } + + While { condition, body } => { + self.add_parent(condition, parent); + self.add_parent(body, parent); + } + + DoWhile { body, condition } => { + self.add_parent(body, parent); + self.add_parent(condition, parent); + } + + ForLoop { + init, + condition, + increment, + body, + } => { + if let Some(init) = init { + self.add_parent(init, parent); + } + + if let Some(condition) = condition { + self.add_parent(condition, parent); + } + + if let Some(increment) = increment { + self.add_parent(increment, parent); + } + + self.add_parent(body, parent); + } + + Goto(..) => {} + Break => {} + Continue => {} + + Return(expr) => { + if let Some(expr) = expr { + self.add_parent(expr, parent); + } + } + + Decls(ref decls) => { + for &decl in decls { + self.add_parent(decl, parent); + } + } + + Asm { .. } => {} + + Attributed { + attributes: _, + substatement, + } => { + self.add_parent(substatement, parent); + } + } + } + + pub fn is_const_stmt(&self, stmt: CStmtId) -> bool { + let is_const = |stmt| self.is_const_stmt(stmt); + let is_const_expr = |expr| self.is_const_expr(expr); + + use CStmtKind::*; + match self[stmt].kind { + Case(expr, stmt, _const_expr) => is_const_expr(expr) && is_const(stmt), + Default(stmt) => is_const(stmt), + Compound(ref stmts) => stmts.iter().copied().all(is_const), + Expr(expr) => is_const_expr(expr), + Empty => true, + If { + scrutinee, + true_variant, + false_variant, + } => { + is_const_expr(scrutinee) + && is_const(true_variant) + && false_variant.map_or(true, is_const) + } + Switch { scrutinee, body } => is_const_expr(scrutinee) && is_const(body), + While { condition, body } => is_const_expr(condition) && is_const(body), + DoWhile { body, condition } => is_const(body) && is_const_expr(condition), + ForLoop { + init, + condition, + increment, + body, + } => { + init.map_or(true, is_const) + && condition.map_or(true, is_const_expr) + && increment.map_or(true, is_const_expr) + && is_const(body) + } + Break => true, + Continue => true, + Return(expr) => expr.map_or(true, is_const_expr), + Decls(ref _decls) => true, + Asm { .. } => false, + Attributed { + attributes: _, + substatement, + } => is_const(substatement), + // `goto`s are tricky, because they can be non-local + // and jump out of the context of the macro. + // A `goto` and its labels are `const` if the whole state machine + // we compile to has all `const` statements, + // but determining what that is exactly is trickier, + // and might depend on the context in which the macro is used. + // This is probably fairly uncommon, so we just assume it's not `const` for now. + // Note that in C, labels are for `goto`s. + // There are no labeled `break`s and `continue`s. + Label(_stmt) => false, + Goto(_label) => false, + } + } +} + +impl Index for TypedAstContext { + type Output = CStmt; + + fn index(&self, index: CStmtId) -> &CStmt { + match self.c_stmts.get(&index) { + None => panic!("Could not find {:?} in TypedAstContext", index), + Some(ty) => ty, + } + } +} diff --git a/c2rust-transpile/src/c_ast/c_type.rs b/c2rust-transpile/src/c_ast/c_type.rs new file mode 100644 index 0000000000..6ba5483487 --- /dev/null +++ b/c2rust-transpile/src/c_ast/c_type.rs @@ -0,0 +1,825 @@ +use crate::c_ast::c_decl::{CDeclId, CDeclKind, CEnumId, CRecordId, CTypedefId}; +use crate::c_ast::c_expr::CExprId; +use crate::c_ast::{Attribute, Located, TypedAstContext}; +use std::fmt::{self, Debug, Display}; +use std::ops::Index; + +pub use c2rust_ast_exporter::clang_ast::{BuiltinVaListKind, SrcFile, SrcLoc, SrcSpan}; + +#[derive(Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Copy, Clone)] +pub struct CTypeId(pub u64); + +// These are references into particular variants of AST nodes +pub type CFuncTypeId = CTypeId; // Function declarations always have types which are 'TypeKind::Function' + +/// Qualified type +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct CQualTypeId { + pub qualifiers: Qualifiers, + pub ctype: CTypeId, +} + +impl CQualTypeId { + pub fn new(ctype: CTypeId) -> Self { + Self { + qualifiers: Qualifiers::default(), + ctype, + } + } +} + +/// Type qualifiers (6.7.3) +#[derive(Debug, Copy, Clone, Default, PartialEq, Eq)] +pub struct Qualifiers { + /// The `const` qualifier, which marks lvalues as non-assignable. + /// + /// We make use of `const` in only two places: + /// * Variable and function bindings (which matches up to Rust's `mut` or not bindings) + /// * The pointed type in pointers (which matches up to Rust's `*const`/`*mut`) + pub is_const: bool, + + pub is_restrict: bool, + + /// The `volatile` qualifier, which prevents the compiler from reordering accesses through such + /// qualified lvalues past other observable side effects (other accesses, or sequence points). + /// + /// The part here about not reordering (or changing in any way) access to something volatile + /// can be replicated in Rust via `std::ptr::read_volatile` and `std::ptr::write_volatile`. + /// Since Rust's execution model is still unclear, I am unsure that we get all of the guarantees + /// `volatile` needs, especially regarding reordering of other side-effects. + /// + /// To see where we use `volatile`, check the call-sites of `Translation::volatile_write` and + /// `Translation::volatile_read`. + pub is_volatile: bool, +} + +impl Qualifiers { + /// Aggregate qualifier information from two sources. + pub fn and(self, other: Qualifiers) -> Qualifiers { + Qualifiers { + is_const: self.is_const || other.is_const, + is_restrict: self.is_restrict || other.is_restrict, + is_volatile: self.is_volatile || other.is_volatile, + } + } +} + +// TODO: these may be interesting, but I'm not sure if they fit here: +// +// * UnaryTransformType +// * AdjustedType + +/// Represents a type in C (6.2.5 Types) +/// +/// Reflects the types in +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum CTypeKind { + Void, + + // Boolean type (6.2.5.2) + Bool, + + // Character type (6.2.5.3) + Char, + + // Signed types (6.2.5.4) + SChar, + Short, + Int, + Long, + LongLong, + + // Unsigned types (6.2.5.6) (actually this also includes `_Bool`) + UChar, + UShort, + UInt, + ULong, + ULongLong, + + // Real floating types (6.2.5.10). Ex: `double` + Float, + Double, + LongDouble, + + // Clang specific types + Int128, + UInt128, + + Complex(CTypeId), + + // Pointer types (6.7.5.1) + Pointer(CQualTypeId), + + // C++ Reference + Reference(CQualTypeId), + + // Array types (6.7.5.2) + // + // A qualifier on an array type means the same thing as a qualifier on its element type. Since + // Clang tracks the qualifiers in both places, we choose to discard qualifiers on the element + // type. + // + // The size expression on a variable-length array is optional, it might be replaced with `*` + ConstantArray(CTypeId, usize), + IncompleteArray(CTypeId), + VariableArray(CTypeId, Option), + + // Type of type or expression (GCC extension) + TypeOf(CTypeId), + TypeOfExpr(CExprId), + + // Function type (6.7.5.3) + // + // Note a function taking no arguments should have one `void` argument. Functions without any + // arguments are in K&R format. + // Flags: is_variable_argument, is_noreturn, has prototype + Function(CQualTypeId, Vec, bool, bool, bool), + + // Type definition type (6.7.7) + Typedef(CTypedefId), + + // Represents a pointer type decayed from an array or function type. + Decayed(CTypeId), + Elaborated(CTypeId), + + // Type wrapped in parentheses + Paren(CTypeId), + + // Struct type + Struct(CRecordId), + + // Union type + Union(CRecordId), + + // Enum definition type + Enum(CEnumId), + + BuiltinFn, + + Attributed(CQualTypeId, Option), + + BlockPointer(CQualTypeId), + + Vector(CQualTypeId, usize), + + Half, + BFloat16, + + // ARM Scalable Vector Extension types + // TODO: represent all the individual types in AArch64SVEACLETypes.def + UnhandledSveType, + + Float128, + // Atomic types (6.7.2.4) + Atomic(CQualTypeId), + + // Rust sized types, pullback'd into C so that we can treat uint16_t, etc. as real types. + Int8, + Int16, + Int32, + Int64, + IntPtr, + UInt8, + UInt16, + UInt32, + UInt64, + UIntPtr, + IntMax, + UIntMax, + Size, + SSize, + PtrDiff, + WChar, +} + +pub type CType = Located; + +impl CTypeKind { + pub const PULLBACK_KINDS: [CTypeKind; 16] = { + use CTypeKind::*; + [ + Int8, Int16, Int32, Int64, IntPtr, UInt8, UInt16, UInt32, UInt64, UIntPtr, IntMax, + UIntMax, Size, SSize, PtrDiff, WChar, + ] + }; + + pub fn is_pointer(&self) -> bool { + matches!(*self, Self::Pointer { .. }) + } + + pub fn is_bool(&self) -> bool { + matches!(*self, Self::Bool) + } + + pub fn is_enum(&self) -> bool { + matches!(*self, Self::Enum { .. }) + } + + pub fn is_integral_type(&self) -> bool { + self.is_unsigned_integral_type() || self.is_signed_integral_type() + } + + pub fn is_unsigned_integral_type(&self) -> bool { + use CTypeKind::*; + matches!( + self, + Bool | UChar + | UInt + | UShort + | ULong + | ULongLong + | UInt128 + | UInt8 + | UInt16 + | UInt32 + | UInt64 + | UIntPtr + | UIntMax + | Size + | WChar + ) + } + + pub fn is_signed_integral_type(&self) -> bool { + use CTypeKind::*; + // `Char` is true on the platforms we handle + matches!( + self, + Char | SChar + | Int + | Short + | Long + | LongLong + | Int128 + | Int8 + | Int16 + | Int32 + | Int64 + | IntPtr + | IntMax + | SSize + | PtrDiff + ) + } + + pub fn is_floating_type(&self) -> bool { + use CTypeKind::*; + matches!(self, Float | Double | LongDouble | Half | BFloat16) + } + + pub fn as_underlying_decl(&self) -> Option { + use CTypeKind::*; + match *self { + Struct(decl_id) | Union(decl_id) | Enum(decl_id) => Some(decl_id), + _ => None, + } + } + + pub fn as_decl_or_typedef(&self) -> Option { + use CTypeKind::*; + match *self { + Typedef(decl_id) | Struct(decl_id) | Union(decl_id) | Enum(decl_id) => Some(decl_id), + _ => None, + } + } + + pub fn is_vector(&self) -> bool { + matches!(self, Self::Vector { .. }) + } + + /// Choose the smaller, simpler of the two types if they are cast-compatible. + pub fn smaller_compatible_type(ty1: CTypeKind, ty2: CTypeKind) -> Option { + let int = Self::is_integral_type; + let float = Self::is_floating_type; + + use CTypeKind::*; + let ty = match (&ty1, &ty2) { + (ty, ty2) if ty == ty2 => ty1, + (Void, _) => ty2, + (Bool, ty) | (ty, Bool) if int(ty) => Bool, + + (Char, ty) | (ty, Char) if int(ty) => Char, + (SChar, ty) | (ty, SChar) if int(ty) => SChar, + (UChar, ty) | (ty, UChar) if int(ty) => UChar, + + (Short, ty) | (ty, Short) if int(ty) => Short, + (UShort, ty) | (ty, UShort) if int(ty) => UShort, + + (Int, ty) | (ty, Int) if int(ty) => Int, + (UInt, ty) | (ty, UInt) if int(ty) => UInt, + + (Float, ty) | (ty, Float) if float(ty) || int(ty) => Float, + + (Long, ty) | (ty, Long) if int(ty) => Long, + (ULong, ty) | (ty, ULong) if int(ty) => ULong, + + (Double, ty) | (ty, Double) if float(ty) || int(ty) => Double, + + (LongLong, ty) | (ty, LongLong) if int(ty) => LongLong, + (ULongLong, ty) | (ty, ULongLong) if int(ty) => ULongLong, + + (LongDouble, ty) | (ty, LongDouble) if float(ty) || int(ty) => LongDouble, + + (Int128, ty) | (ty, Int128) if int(ty) => Int128, + (UInt128, ty) | (ty, UInt128) if int(ty) => UInt128, + + // Integer to pointer conversion. We want to keep the integer and + // cast to a pointer at use. + (Pointer(_), ty) if int(ty) => ty2, + (ty, Pointer(_)) if int(ty) => ty1, + + // Array to pointer decay. We want to use the array and push the + // decay to the use of the value. + (Pointer(ptr_ty), ConstantArray(arr_ty, _)) + | (Pointer(ptr_ty), IncompleteArray(arr_ty)) + | (Pointer(ptr_ty), VariableArray(arr_ty, _)) + if ptr_ty.ctype == *arr_ty => + { + ty2 + } + (ConstantArray(arr_ty, _), Pointer(ptr_ty)) + | (IncompleteArray(arr_ty), Pointer(ptr_ty)) + | (VariableArray(arr_ty, _), Pointer(ptr_ty)) + if ptr_ty.ctype == *arr_ty => + { + ty1 + } + + _ => return None, + }; + Some(ty) + } + + /// Return the element type of a pointer or array + pub fn element_ty(&self) -> Option { + Some(match *self { + Self::Pointer(ty) => ty.ctype, + Self::ConstantArray(ty, _) => ty, + Self::IncompleteArray(ty) => ty, + Self::VariableArray(ty, _) => ty, + _ => return None, + }) + } + + pub fn as_str(&self) -> &'static str { + use CTypeKind::*; + match self { + Void => "void", + Bool => "_Bool", + Char => "char", + SChar => "signed char", + Short => "signed short", + Int => "int", + Long => "long", + LongLong => "long long", + UChar => "unsigned char", + UShort => "unsigned short", + UInt => "unsigned int", + ULong => "unsigned long", + ULongLong => "unsigned long long", + Float => "float", + Double => "double", + LongDouble => "long double", + Int128 => "__int128", + UInt128 => "unsigned __int128", + Half => "half", + BFloat16 => "bfloat16", + Float128 => "__float128", + + Int8 => "int8_t", + Int16 => "int16_t", + Int32 => "int32_t", + Int64 => "int64_t", + IntPtr => "intptr_t", + UInt8 => "uint8_t", + UInt16 => "uint16_t", + UInt32 => "uint32_t", + UInt64 => "uint64_t", + UIntPtr => "uintptr_t", + IntMax => "intmax_t", + UIntMax => "uintmax_t", + Size => "size_t", + SSize => "ssize_t", + PtrDiff => "ptrdiff_t", + WChar => "wchar_t", + + _ => unimplemented!("Printer::print_type({:?})", self), + } + } + + /// Whether `value` is guaranteed to be in this integer type's range. + /// Thus, the narrowest possible range is used. + /// + /// For example, for [`Self::Long`], [`i32`]'s range is used, + /// as on Linux and macOS (LP64), it's an [`i64`], + /// but on Windows (LLP64), it's only an [`i32`]. + /// + /// This should only be called on integer types. + /// Other types will return `false`. + pub fn guaranteed_integer_in_range(&self, value: u64) -> bool { + fn in_range>(value: u64) -> bool { + T::try_from(value).is_ok() + } + + use CTypeKind::*; + match *self { + Void => false, + + // Kind of an integer type, but would definitely need an explicit cast. + Bool => false, + + // Can be signed or unsigned, so choose the minimum range of each. + Char => (u8::MIN as u64..=i8::MAX as u64).contains(&value), + WChar => in_range::(value), + + // `int` is at least `i16` and `long` is at least `i32`. + SChar => in_range::(value), + Short => in_range::(value), + Int => in_range::(value), + Long => in_range::(value), + LongLong => in_range::(value), + + // `unsigned int` is at least `u16` and `unsigned long` is at least `u32`. + UChar => in_range::(value), + UShort => in_range::(value), + UInt => in_range::(value), + ULong => in_range::(value), + ULongLong => in_range::(value), + + Int8 => in_range::(value), + Int16 => in_range::(value), + Int32 => in_range::(value), + Int64 => in_range::(value), + Int128 => in_range::(value), + + UInt8 => in_range::(value), + UInt16 => in_range::(value), + UInt32 => in_range::(value), + UInt64 => in_range::(value), + UInt128 => in_range::(value), + + // There's no guarantee on pointer size, but `NULL` should work. + IntPtr => value == 0, + UIntPtr => value == 0, + + IntMax => in_range::(value), + UIntMax => in_range::(value), + + // `size_t` is at least a `u16`, and similar for `ssize_t` and `ptrdiff_t`. + Size => in_range::(value), + SSize => in_range::(value), + PtrDiff => in_range::(value), + + // Floats, see `Self::guaranteed_float_in_range`. + Float => false, + Double => false, + LongDouble => false, + Half => false, + BFloat16 => false, + Float128 => false, + + // Non-scalars. + Complex(_) => false, + Pointer(_) => false, + Reference(_) => false, + ConstantArray(_, _) => false, + IncompleteArray(_) => false, + VariableArray(_, _) => false, + TypeOf(_) => false, + TypeOfExpr(_) => false, + Function(_, _, _, _, _) => false, + Typedef(_) => false, + Decayed(_) => false, + Elaborated(_) => false, + Paren(_) => false, + Struct(_) => false, + Union(_) => false, + Enum(_) => false, + BuiltinFn => false, + Attributed(_, _) => false, + BlockPointer(_) => false, + Vector(_, _) => false, + UnhandledSveType => false, + Atomic(_) => false, + } + } + + /// See [`Self::guaranteed_integer_in_range`]. + /// This is the same, but for floats. + /// + /// This should only be called on float types. + /// Other types will return `false`. + pub fn guaranteed_float_in_range(&self, value: f64) -> bool { + fn in_range>(value: f64) -> bool { + T::try_from(value).is_ok() + } + + use CTypeKind::*; + match *self { + // `f32: TryFrom` is not implemented. + // C `float`s are not guaranteed to be `f32`, + // but Rust (namely `libc`) doesn't support any platform where this isn't the case. + Float => value >= f32::MIN as f64 && value <= f32::MAX as f64, + + // Similarly to `float`, `double` is not guaranteed to be `f64`, + // but `libc` doesn't support any platform where this isn't the case. + Double => in_range::(value), + + // `long double` (not `f128`) is only guaranteed to be at least as precise as a `double`. + LongDouble => in_range::(value), + + // All `f64`s are valid `f128`s. + Float128 => in_range::(value), + + // TODO Would like to depend on `half`. + Half => todo!("f16 range"), + BFloat16 => todo!("bf16 range"), + + Void => false, + Bool => false, + Char => false, + SChar => false, + Short => false, + Int => false, + Long => false, + LongLong => false, + UChar => false, + UShort => false, + UInt => false, + ULong => false, + ULongLong => false, + Int128 => false, + UInt128 => false, + Complex(_) => false, + Pointer(_) => false, + Reference(_) => false, + ConstantArray(_, _) => false, + IncompleteArray(_) => false, + VariableArray(_, _) => false, + TypeOf(_) => false, + TypeOfExpr(_) => false, + Function(_, _, _, _, _) => false, + Typedef(_) => false, + Decayed(_) => false, + Elaborated(_) => false, + Paren(_) => false, + Struct(_) => false, + Union(_) => false, + Enum(_) => false, + BuiltinFn => false, + Attributed(_, _) => false, + BlockPointer(_) => false, + Vector(_, _) => false, + UnhandledSveType => false, + Atomic(_) => false, + Int8 => false, + Int16 => false, + Int32 => false, + Int64 => false, + IntPtr => false, + UInt8 => false, + UInt16 => false, + UInt32 => false, + UInt64 => false, + UIntPtr => false, + IntMax => false, + UIntMax => false, + Size => false, + SSize => false, + PtrDiff => false, + WChar => false, + } + } +} + +impl Display for CTypeKind { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.as_str()) + } +} + +impl TypedAstContext { + /// Predicate for struct, union, and enum declarations without + /// bodies. These forward declarations are suitable for use as + /// the targets of pointers + pub fn is_forward_declared_type(&self, typ: CTypeId) -> bool { + use CDeclKind::*; + || -> Option<()> { + let decl_id = self.resolve_type(typ).kind.as_underlying_decl()?; + matches!( + self[decl_id].kind, + Struct { fields: None, .. } + | Union { fields: None, .. } + | Enum { + integral_type: None, + .. + } + ) + .then(|| ()) + }() + .is_some() + } + + /// Returns whether `typ` is a chain of typedefs that ends in `__builtin_va_list`, + /// thus naming the type clang uses to represent `va_list`s. + /// This works on all architectures, but does not work in situations where typedefs are + /// resolved/bypassed, such as with array-to-pointer decay. + pub fn is_builtin_va_list(&self, mut typ: CTypeId) -> bool { + loop { + // Skip over Elaborated types + let mut kind = &self.index(typ).kind; + + while let &CTypeKind::Elaborated(typ) = kind { + kind = &self.index(typ).kind; + } + + // TODO: Rust 1.65: use let-else + let decl = match kind { + &CTypeKind::Typedef(decl) => decl, + _ => return false, + }; + let (name, qtyp) = match &self.index(decl).kind { + &CDeclKind::Typedef { ref name, typ, .. } => (name, typ), + _ => panic!("Typedef decl did not point to a typedef"), + }; + + if name == "__builtin_va_list" { + return true; + } + + typ = qtyp.ctype; + } + } + + /// Returns whether `typ` is the architecture-specific type used for `va_list`. + /// Returns `false` for architectures where `va_list` is a generic pointer type. + pub fn is_va_list_struct(&self, typ: CTypeId) -> bool { + use BuiltinVaListKind::*; + + match self.va_list_kind { + // No special identification is possible with generic types. + CharPtrBuiltinVaList | VoidPtrBuiltinVaList => false, + + // ARM32: + // typedef struct __va_list { + // void *__ap; + // } __builtin_va_list; + + // ARM64: + // typedef struct __va_list { + // void *__stack; + // void *__gr_top; + // void *__vr_top; + // int __gr_offs; + // int __vr_offs; + // } __builtin_va_list; + AAPCSABIBuiltinVaList | AArch64ABIBuiltinVaList => { + // TODO: Rust 1.65: use let-else + let decl = match self.resolve_type(typ).kind { + CTypeKind::Struct(decl) => decl, + _ => return false, + }; + let name = match &self[decl].kind { + CDeclKind::Struct { + name: Some(name), .. + } => name, + _ => return false, + }; + + name == "__va_list" + } + + // X86-64: + // typedef struct __va_list_tag { + // unsigned int gp_offset; + // unsigned int fp_offset; + // void *overflow_arg_area; + // void *reg_save_area; + // } __builtin_va_list[1]; + + // Power: + // typedef struct __va_list_tag { + // unsigned char gpr; + // unsigned char fpr; + // unsigned short reserved; + // char *overflow_arg_area; + // char *reg_save_area; + // } __builtin_va_list[1]; + X86_64ABIBuiltinVaList | PowerABIBuiltinVaList => { + // TODO: Rust 1.65: use let-else + let inner = match self.resolve_type(typ).kind { + CTypeKind::ConstantArray(inner, 1) => inner, + // Account for array-to-pointer decay in function parameters. + CTypeKind::Pointer(CQualTypeId { ctype: inner, .. }) => inner, + _ => return false, + }; + let decl = match self.resolve_type(inner).kind { + CTypeKind::Struct(decl) => decl, + _ => return false, + }; + let name = match &self[decl].kind { + CDeclKind::Struct { + name: Some(name), .. + } => name, + _ => return false, + }; + + name == "__va_list_tag" + } + + kind => unimplemented!("va_list type {:?} not yet implemented", kind), + } + } + + /// Returns whether `typ` is a C `va_list`. + pub fn is_va_list(&self, typ: CTypeId) -> bool { + self.is_builtin_va_list(typ) || self.is_va_list_struct(typ) + } + + /// Predicate for function pointers + pub fn is_function_pointer(&self, typ: CTypeId) -> bool { + let resolved_ctype = self.resolve_type(typ); + use CTypeKind::*; + if let Pointer(p) = resolved_ctype.kind { + matches!(self.resolve_type(p.ctype).kind, Function { .. }) + } else { + false + } + } + + /// Returns the length of an array type, or panics. + pub fn array_len(&self, typ: CTypeId) -> usize { + match self.resolve_type(typ).kind { + CTypeKind::ConstantArray(_, len) => len, + ref kind => panic!("CTypeId {typ:?} is {kind:?}, not a ConstantArray"), + } + } + + /// Can the given field decl be a flexible array member? + pub fn maybe_flexible_array(&self, typ: CTypeId) -> bool { + let field_ty = self.resolve_type(typ); + use CTypeKind::*; + matches!(field_ty.kind, IncompleteArray(_) | ConstantArray(_, 0 | 1)) + } + + pub fn get_pointee_qual_type(&self, typ: CTypeId) -> Option { + let resolved_ctype = self.resolve_type(typ); + if let CTypeKind::Pointer(p) = resolved_ctype.kind { + Some(p) + } else { + None + } + } + + pub fn type_for_kind(&self, kind: &CTypeKind) -> Option { + self.c_types + .iter() + .find_map(|(id, k)| if kind == &k.kind { Some(*id) } else { None }) + } + + pub fn resolve_type_id(&self, typ: CTypeId) -> CTypeId { + use CTypeKind::*; + let ty = match self.index(typ).kind { + Attributed(ty, _) => ty.ctype, + Elaborated(ty) => ty, + Decayed(ty) => ty, + TypeOf(ty) => ty, + Paren(ty) => ty, + Typedef(decl) => match self.index(decl).kind { + CDeclKind::Typedef { typ: ty, .. } => ty.ctype, + _ => panic!("Typedef decl did not point to a typedef"), + }, + _ => return typ, + }; + self.resolve_type_id(ty) + } + + pub fn resolve_type(&self, typ: CTypeId) -> &CType { + let resolved_typ_id = self.resolve_type_id(typ); + self.index(resolved_typ_id) + } + + pub fn is_aligned_struct_type(&self, typ: CTypeId) -> bool { + if let Some(decl_id) = self.resolve_type(typ).kind.as_underlying_decl() { + if let CDeclKind::Struct { + manual_alignment: Some(_), + .. + } = self.index(decl_id).kind + { + return true; + } + } + false + } +} + +impl Index for TypedAstContext { + type Output = CType; + + fn index(&self, index: CTypeId) -> &CType { + match self.c_types.get(&index) { + None => panic!("Could not find {:?} in TypedAstContext", index), + Some(ty) => ty, + } + } +} diff --git a/c2rust-transpile/src/c_ast/conversion.rs b/c2rust-transpile/src/c_ast/conversion.rs index 59cc7bcfa8..0b3ac5429b 100644 --- a/c2rust-transpile/src/c_ast/conversion.rs +++ b/c2rust-transpile/src/c_ast/conversion.rs @@ -1,9 +1,20 @@ -use crate::c_ast::*; +use crate::c_ast::c_decl::{CDecl, CDeclId, CDeclKind}; +use crate::c_ast::c_expr::{ + CBinOp, CExpr, CExprId, CExprKind, CLiteral, CUnOp, CUnTypeOp, CastKind, ConstIntExpr, + Designator, IntBase, MemberKind, OffsetOfKind, +}; +use crate::c_ast::c_stmt::{AsmOperand, CStmt, CStmtId, CStmtKind}; +use crate::c_ast::c_type::{CQualTypeId, CType, CTypeId, CTypeKind, Qualifiers}; +use crate::c_ast::iterators::{immediate_children_all_types, NodeVisitor}; +use crate::c_ast::{Attribute, DisplaySrcSpan, SomeId, TypedAstContext}; use crate::diagnostics::diag; use c2rust_ast_exporter::clang_ast::*; use failure::err_msg; +use indexmap::IndexSet; use serde_bytes::ByteBuf; -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; +use std::mem; +use std::path::Path; use std::rc::Rc; use std::vec::Vec; @@ -388,17 +399,23 @@ impl ConversionContext { /// Add a `CStmt` node into the `TypedAstContext` fn add_stmt(&mut self, id: ImporterId, stmt: CStmt) { - self.typed_context.c_stmts.insert(CStmtId(id), stmt); + let id = CStmtId(id); + self.typed_context.add_stmt_parents(id, &stmt.kind); + self.typed_context.c_stmts.insert(id, stmt); } /// Add a `CExpr` node into the `TypedAstContext` fn add_expr(&mut self, id: ImporterId, expr: CExpr) { - self.typed_context.c_exprs.insert(CExprId(id), expr); + let id = CExprId(id); + self.typed_context.add_expr_parents(id, &expr.kind); + self.typed_context.c_exprs.insert(id, expr); } /// Add a `CDecl` node into the `TypedAstContext` fn add_decl(&mut self, id: ImporterId, decl: CDecl) { - self.typed_context.c_decls.insert(CDeclId(id), decl); + let id = CDeclId(id); + self.typed_context.add_decl_parents(id, &decl.kind); + self.typed_context.c_decls.insert(id, decl); } /// Clang has `Expression <: Statement`, but we want to make that explicit via the @@ -554,6 +571,116 @@ impl ConversionContext { self.typed_context.va_list_kind = untyped_context.va_list_kind; self.typed_context.target = untyped_context.target.clone(); + + self.bubble_expr_types(); + } + + /// Bubble types of unary and binary operators up from their args into the expression type. + /// + /// In Clang 15 and below, the Clang AST resolves typedefs in the expression type of unary and + /// binary expressions. For example, a BinaryExpr node adding two `size_t` expressions will be + /// given an `unsigned long` type rather than the `size_t` typedef type. This behavior changed + /// in Clang 16. This method adjusts AST node types to match those produced by Clang 16 and + /// newer; on these later Clang versions, it should have no effect. + /// + /// This pass is necessary because we reify some typedef types (such as `size_t`) into their own + /// distinct Rust types. As such, we need to make sure we know the exact type to generate when + /// we translate an expr, not just its resolved type (looking through typedefs). + fn bubble_expr_types(&mut self) { + struct BubbleExprTypes<'a> { + ast_context: &'a mut TypedAstContext, + } + + impl<'a> NodeVisitor for BubbleExprTypes<'a> { + fn children(&mut self, id: SomeId) -> Vec { + immediate_children_all_types(self.ast_context, id) + } + + fn post(&mut self, id: SomeId) { + let e = match id { + SomeId::Expr(e) => e, + _ => return, + }; + + let new_ty = match self.ast_context.c_exprs[&e].kind { + CExprKind::Conditional(_ty, _cond, lhs, rhs) => { + let lhs_type_id = + self.ast_context.c_exprs[&lhs].kind.get_qual_type().unwrap(); + let rhs_type_id = + self.ast_context.c_exprs[&rhs].kind.get_qual_type().unwrap(); + + let lhs_resolved_ty = self.ast_context.resolve_type(lhs_type_id.ctype); + let rhs_resolved_ty = self.ast_context.resolve_type(rhs_type_id.ctype); + + if CTypeKind::PULLBACK_KINDS.contains(&lhs_resolved_ty.kind) { + Some(lhs_type_id) + } else if CTypeKind::PULLBACK_KINDS.contains(&rhs_resolved_ty.kind) { + Some(rhs_type_id) + } else { + None + } + } + CExprKind::Binary(_ty, op, lhs, rhs, _, _) => { + let rhs_type_id = + self.ast_context.c_exprs[&rhs].kind.get_qual_type().unwrap(); + let lhs_kind = &self.ast_context.c_exprs[&lhs].kind; + let lhs_type_id = lhs_kind.get_qual_type().unwrap(); + + let lhs_resolved_ty = self.ast_context.resolve_type(lhs_type_id.ctype); + let rhs_resolved_ty = self.ast_context.resolve_type(rhs_type_id.ctype); + + let neither_ptr = !lhs_resolved_ty.kind.is_pointer() + && !rhs_resolved_ty.kind.is_pointer(); + + if op.all_types_same() && neither_ptr { + if CTypeKind::PULLBACK_KINDS.contains(&lhs_resolved_ty.kind) { + Some(lhs_type_id) + } else { + Some(rhs_type_id) + } + } else if op == CBinOp::ShiftLeft || op == CBinOp::ShiftRight { + Some(lhs_type_id) + } else { + return; + } + } + CExprKind::Unary(_ty, op, e, _idk) => op.expected_result_type( + self.ast_context, + self.ast_context.c_exprs[&e].kind.get_qual_type().unwrap(), + ), + CExprKind::Paren(_ty, e) => self.ast_context.c_exprs[&e].kind.get_qual_type(), + CExprKind::UnaryType(_, op, _, _) => { + // All of these `CUnTypeOp`s should return `size_t`. + let kind = match op { + CUnTypeOp::SizeOf => CTypeKind::Size, + CUnTypeOp::AlignOf => CTypeKind::Size, + CUnTypeOp::PreferredAlignOf => CTypeKind::Size, + }; + let ty = self + .ast_context + .type_for_kind(&kind) + .expect("CTypeKind::Size should be size_t"); + Some(CQualTypeId::new(ty)) + } + _ => return, + }; + let ty = self + .ast_context + .c_exprs + .get_mut(&e) + .and_then(|e| e.kind.get_qual_type_mut()); + if let (Some(ty), Some(new_ty)) = (ty, new_ty) { + *ty = new_ty; + }; + } + } + + for decl in self.typed_context.c_decls_top.clone() { + BubbleExprTypes { + ast_context: &mut self.typed_context, + } + .visit_tree(SomeId::Decl(decl)); + } } /// Visit child nodes of a `RecordDecl` (`struct` or `union`) and collect `FieldDecl` node IDs. @@ -561,7 +688,6 @@ impl ConversionContext { &'a mut self, untyped_context: &'a AstContext, node: &'a AstNode, - new_id: ImporterId, ) -> impl Iterator + 'a { use self::node_types::*; @@ -573,7 +699,6 @@ impl ConversionContext { .expect("child node not found"); let id = CDeclId(self.visit_node_type(decl, FIELD_DECL | ENUM_DECL | RECORD_DECL)); - self.typed_context.parents.insert(id, CDeclId(new_id)); if decl_node.tag == ASTEntryTag::TagFieldDecl { Some(id) @@ -1366,30 +1491,30 @@ impl ConversionContext { .expect("Expected operator") .as_str() { - "&" => UnOp::AddressOf, - "*" => UnOp::Deref, - "+" => UnOp::Plus, - "-" => UnOp::Negate, - "~" => UnOp::Complement, - "!" => UnOp::Not, + "&" => CUnOp::AddressOf, + "*" => CUnOp::Deref, + "+" => CUnOp::Plus, + "-" => CUnOp::Negate, + "~" => CUnOp::Complement, + "!" => CUnOp::Not, "++" => { if prefix { - UnOp::PreIncrement + CUnOp::PreIncrement } else { - UnOp::PostIncrement + CUnOp::PostIncrement } } "--" => { if prefix { - UnOp::PreDecrement + CUnOp::PreDecrement } else { - UnOp::PostDecrement + CUnOp::PostDecrement } } - "__real" => UnOp::Real, - "__imag" => UnOp::Imag, - "__extension__" => UnOp::Extension, - "co_await" => UnOp::Coawait, + "__real" => CUnOp::Real, + "__imag" => CUnOp::Imag, + "__extension__" => CUnOp::Extension, + "co_await" => CUnOp::Coawait, o => panic!("Unexpected operator: {}", o), }; @@ -1495,36 +1620,36 @@ impl ConversionContext { .expect("Expected operator") .as_str() { - "*" => BinOp::Multiply, - "/" => BinOp::Divide, - "%" => BinOp::Modulus, - "+" => BinOp::Add, - "-" => BinOp::Subtract, - "<<" => BinOp::ShiftLeft, - ">>" => BinOp::ShiftRight, - "<" => BinOp::Less, - ">" => BinOp::Greater, - "<=" => BinOp::LessEqual, - ">=" => BinOp::GreaterEqual, - "==" => BinOp::EqualEqual, - "!=" => BinOp::NotEqual, - "&" => BinOp::BitAnd, - "^" => BinOp::BitXor, - "|" => BinOp::BitOr, - "&&" => BinOp::And, - "||" => BinOp::Or, - "+=" => BinOp::AssignAdd, - "-=" => BinOp::AssignSubtract, - "*=" => BinOp::AssignMultiply, - "/=" => BinOp::AssignDivide, - "%=" => BinOp::AssignModulus, - "^=" => BinOp::AssignBitXor, - "<<=" => BinOp::AssignShiftLeft, - ">>=" => BinOp::AssignShiftRight, - "|=" => BinOp::AssignBitOr, - "&=" => BinOp::AssignBitAnd, - "=" => BinOp::Assign, - "," => BinOp::Comma, + "*" => CBinOp::Multiply, + "/" => CBinOp::Divide, + "%" => CBinOp::Modulus, + "+" => CBinOp::Add, + "-" => CBinOp::Subtract, + "<<" => CBinOp::ShiftLeft, + ">>" => CBinOp::ShiftRight, + "<" => CBinOp::Less, + ">" => CBinOp::Greater, + "<=" => CBinOp::LessEqual, + ">=" => CBinOp::GreaterEqual, + "==" => CBinOp::EqualEqual, + "!=" => CBinOp::NotEqual, + "&" => CBinOp::BitAnd, + "^" => CBinOp::BitXor, + "|" => CBinOp::BitOr, + "&&" => CBinOp::And, + "||" => CBinOp::Or, + "+=" => CBinOp::AssignAdd, + "-=" => CBinOp::AssignSubtract, + "*=" => CBinOp::AssignMultiply, + "/=" => CBinOp::AssignDivide, + "%=" => CBinOp::AssignModulus, + "^=" => CBinOp::AssignBitXor, + "<<=" => CBinOp::AssignShiftLeft, + ">>=" => CBinOp::AssignShiftRight, + "|=" => CBinOp::AssignBitOr, + "&=" => CBinOp::AssignBitAnd, + "=" => CBinOp::Assign, + "," => CBinOp::Comma, _ => unimplemented!(), }; @@ -1629,9 +1754,9 @@ impl ConversionContext { let kind_name = from_value::(node.extras[0].clone()).expect("expected kind"); let kind = match kind_name.as_str() { - "sizeof" => UnTypeOp::SizeOf, - "alignof" => UnTypeOp::AlignOf, - "preferredalignof" => UnTypeOp::PreferredAlignOf, + "sizeof" => CUnTypeOp::SizeOf, + "alignof" => CUnTypeOp::AlignOf, + "preferredalignof" => CUnTypeOp::PreferredAlignOf, str => panic!("Unsupported operation: {}", str), }; @@ -2164,9 +2289,7 @@ impl ConversionContext { .iter() .map(|id| { let con = id.expect("Enum constant not found"); - let id = CDeclId(self.visit_node_type(con, ENUM_CON)); - self.typed_context.parents.insert(id, CDeclId(new_id)); - id + CDeclId(self.visit_node_type(con, ENUM_CON)) }) .collect(); @@ -2269,10 +2392,7 @@ impl ConversionContext { from_value(node.extras[6].clone()).expect("Expected struct alignment"); let fields: Option> = if has_def { - Some( - self.visit_record_children(untyped_context, node, new_id) - .collect(), - ) + Some(self.visit_record_children(untyped_context, node).collect()) } else { None }; @@ -2300,10 +2420,7 @@ impl ConversionContext { let attrs = from_value::>(node.extras[2].clone()) .expect("Expected attribute array on record"); let fields: Option> = if has_def { - Some( - self.visit_record_children(untyped_context, node, new_id) - .collect(), - ) + Some(self.visit_record_children(untyped_context, node).collect()) } else { None }; diff --git a/c2rust-transpile/src/c_ast/iterators.rs b/c2rust-transpile/src/c_ast/iterators.rs index 38bcffe6a8..b73296d984 100644 --- a/c2rust-transpile/src/c_ast/iterators.rs +++ b/c2rust-transpile/src/c_ast/iterators.rs @@ -1,35 +1,8 @@ -use crate::c_ast::*; - -#[derive(Copy, Clone, Eq, PartialEq, Debug, Hash)] -pub enum SomeId { - Stmt(CStmtId), - Expr(CExprId), - Decl(CDeclId), - Type(CTypeId), -} - -macro_rules! from_some_id { - ( $field_type:ty, $con_name:ident, $proj_name:ident ) => { - impl From<$field_type> for SomeId { - fn from(a: $field_type) -> Self { - SomeId::$con_name(a) - } - } - impl SomeId { - pub fn $proj_name(self) -> Option<$field_type> { - match self { - SomeId::$con_name(x) => Some(x), - _ => None, - } - } - } - }; -} - -from_some_id!(CExprId, Expr, expr); -from_some_id!(CStmtId, Stmt, stmt); -from_some_id!(CDeclId, Decl, decl); -from_some_id!(CTypeId, Type, type_); +use crate::c_ast::c_decl::CDeclKind; +use crate::c_ast::c_expr::{CExprKind, OffsetOfKind}; +use crate::c_ast::c_stmt::CStmtKind; +use crate::c_ast::c_type::CTypeKind; +use crate::c_ast::{SomeId, TypedAstContext}; /// Like the vec macro except that it calls the into method on all list elements macro_rules! intos { @@ -37,7 +10,7 @@ macro_rules! intos { } fn immediate_expr_children(kind: &CExprKind) -> Vec { - use crate::c_ast::CExprKind::*; + use CExprKind::*; match *kind { BadExpr => vec![], DesignatedInitExpr(..) => vec![], // the relevant information will be found in the semantic initializer @@ -83,7 +56,7 @@ fn immediate_expr_children(kind: &CExprKind) -> Vec { } fn immediate_expr_children_all_types(kind: &CExprKind) -> Vec { - use crate::c_ast::CExprKind::*; + use CExprKind::*; match *kind { BadExpr => vec![], DesignatedInitExpr(..) => vec![], // the relevant information will be found in the semantic initializer @@ -144,7 +117,7 @@ fn immediate_expr_children_all_types(kind: &CExprKind) -> Vec { } fn immediate_decl_children(kind: &CDeclKind) -> Vec { - use crate::c_ast::CDeclKind::*; + use CDeclKind::*; match *kind { Function { typ, @@ -189,7 +162,7 @@ fn immediate_decl_children(kind: &CDeclKind) -> Vec { } fn immediate_stmt_children(kind: &CStmtKind) -> Vec { - use crate::c_ast::CStmtKind::*; + use CStmtKind::*; match *kind { Expr(e) => intos![e], Label(s) => intos![s], @@ -270,7 +243,7 @@ fn immediate_stmt_children(kind: &CStmtKind) -> Vec { } fn immediate_type_children(kind: &CTypeKind) -> Vec { - use crate::c_ast::CTypeKind::*; + use CTypeKind::*; match *kind { Elaborated(_) => vec![], // These are references to previous definitions TypeOfExpr(e) => intos![e], diff --git a/c2rust-transpile/src/c_ast/mod.rs b/c2rust-transpile/src/c_ast/mod.rs index c01937bf6c..96b4525d3b 100644 --- a/c2rust-transpile/src/c_ast/mod.rs +++ b/c2rust-transpile/src/c_ast/mod.rs @@ -1,47 +1,32 @@ -use crate::c_ast::iterators::{immediate_children_all_types, NodeVisitor}; -use crate::iterators::{DFNodes, SomeId}; -use c2rust_ast_exporter::clang_ast::LRValue; -use indexmap::{IndexMap, IndexSet}; +use crate::c_ast::c_decl::{CDecl, CDeclId, CDeclKind}; +use crate::c_ast::c_expr::{CExpr, CExprId, CExprKind}; +use crate::c_ast::c_stmt::{CLabelId, CStmt, CStmtId}; +use crate::c_ast::c_type::{CType, CTypeId, CTypeKind}; +use crate::c_ast::iterators::DFNodes; +use indexmap::IndexMap; use itertools::Itertools; use std::cell::RefCell; use std::cmp::{Ordering, Reverse}; use std::collections::{HashMap, HashSet}; use std::fmt::{self, Debug, Display}; +use std::iter; +use std::mem; use std::ops::Index; use std::path::{Path, PathBuf}; use std::rc::Rc; -use std::{iter, mem}; pub use self::conversion::*; pub use self::print::Printer; pub use c2rust_ast_exporter::clang_ast::{BuiltinVaListKind, SrcFile, SrcLoc, SrcSpan}; +pub mod c_decl; +pub mod c_expr; +pub mod c_stmt; +pub mod c_type; mod conversion; pub mod iterators; mod print; -#[derive(Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Copy, Clone)] -pub struct CTypeId(pub u64); - -#[derive(Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Copy, Clone)] -pub struct CExprId(pub u64); - -#[derive(Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Copy, Clone)] -pub struct CDeclId(pub u64); - -#[derive(Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Copy, Clone)] -pub struct CStmtId(pub u64); - -// These are references into particular variants of AST nodes -pub type CLabelId = CStmtId; // Labels point into the 'StmtKind::Label' that declared the label -pub type CFieldId = CDeclId; // Records always contain 'DeclKind::Field's -pub type CParamId = CDeclId; // Parameters always contain 'DeclKind::Variable's -pub type CFuncTypeId = CTypeId; // Function declarations always have types which are 'TypeKind::Function' -pub type CRecordId = CDeclId; // Record types need to point to 'DeclKind::Record' -pub type CTypedefId = CDeclId; // Typedef types need to point to 'DeclKind::Typedef' -pub type CEnumId = CDeclId; // Enum types need to point to 'DeclKind::Enum' -pub type CEnumConstantId = CDeclId; // Enum's need to point to child 'DeclKind::EnumConstant's - /// AST context containing all of the nodes in the Clang AST #[derive(Debug, Clone, Default)] pub struct TypedAstContext { @@ -55,12 +40,16 @@ pub struct TypedAstContext { /// iterated over export all defined types during translation. c_decls: IndexMap, + /// The parent nodes of each CExprId, CStmtId or CDeclId node. + /// Most nodes have exactly one parent, or zero if they are a top-level node. + /// + /// Expressions appearing inside an `InitListExpr` can have two parents, + /// as they are shared between the semantic form and the syntactic form. + parents: HashMap>, + pub c_decls_top: Vec, pub c_main: Option, - /// record fields and enum constants - pub parents: HashMap, - /// Mapping from [`FileId`] to [`SrcFile`]. Deduplicated by file path. files: Vec, @@ -175,18 +164,6 @@ impl PartialOrd for SrcLocInclude<'_> { } } -/// The range of source code locations for a C declaration. -#[derive(Copy, Clone)] -pub struct CDeclSrcRange { - /// The earliest position where this declaration or its documentation might start. - pub earliest_begin: SrcLoc, - /// A position by which this declaration itself is known to have begun. - /// Attributes or return type may possibly precede this position. - pub strict_begin: SrcLoc, - /// The end of the declaration, except for possible trailing semicolon. - pub end: SrcLoc, -} - impl TypedAstContext { // TODO: build the TypedAstContext during initialization, rather than // building an empty one and filling it later. @@ -240,6 +217,32 @@ impl TypedAstContext { } } + fn add_parent(&mut self, child: impl Into, parent: impl Into) { + self.parents + .entry(child.into()) + .or_default() + .push(parent.into()); + } + + /// Returns the parent nodes of `child`. + pub fn parents(&self, child: impl Into) -> &[SomeId] { + self.parents + .get(&child.into()) + .map(AsRef::as_ref) + .unwrap_or_default() + } + + /// If `child` has a parent node, returns the first one. + pub fn parent(&self, child: impl Into) -> Option { + self.parents(child).get(0).copied() + } + + /// If `child` has a parent node, and the first one is of the given type `T`, returns it. + pub fn parent_with_type>(&self, child: impl Into) -> Option { + self.parent(child) + .and_then(|parent| T::try_from(parent).ok()) + } + pub fn display_loc(&self, loc: &Option) -> Option { loc.as_ref().map(|loc| DisplaySrcSpan { file: self.files[self.file_map[loc.fileid as usize]].path.clone(), @@ -338,664 +341,6 @@ impl TypedAstContext { } } - /// Construct a map from top-level decls in the main file to their source ranges. - pub fn top_decl_locs(&self) -> IndexMap { - let mut name_loc_map = IndexMap::new(); - let mut prev_end_loc = SrcLoc { - fileid: 0, - line: 0, - column: 0, - }; - // Sort decls by source location so we can reason about the possibly comment-containing gaps - // between them. - let mut decls_sorted = self.c_decls_top.clone(); - // Break ties in `begin_loc` (e.g. from `int a, b;`) using `end_loc`. - decls_sorted - .sort_by_key(|decl| (self.c_decls[decl].begin_loc(), self.c_decls[decl].end_loc())); - for decl_id in &decls_sorted { - let decl = &self.c_decls[decl_id]; - let begin_loc: SrcLoc = decl.begin_loc().expect("no begin loc for top-level decl"); - let end_loc: SrcLoc = decl.end_loc().expect("no end loc for top-level decl"); - - // Skip fileid 0; this is not a real file, so these source locations aren't important. - if begin_loc.fileid == 0 { - continue; - } - if begin_loc == end_loc { - log::warn!( - "zero-length source range for top-level decl; skipping. source ranges for \ - top-level decls may be incorrect.\ndecl: {decl:?}" - ); - continue; - } - - // If encountering a new file, reset end of last top-level decl. - if prev_end_loc.fileid != begin_loc.fileid { - prev_end_loc = SrcLoc { - fileid: begin_loc.fileid, - line: 1, - column: 1, - } - } - - // This definition ends before the previous one does, i.e. it is nested. - // This does not generally occur for regular definitions, e.g. variables within - // functions, because the variables will not be top-level decls. But it can occur - // for macros defined inside functions, since all macros are top-level decls! - let is_nested = end_loc < prev_end_loc; - - // Clang emits declarations of builtins (and opaque types such as when encountering - // `struct undeclared *`) at their first usage site. This means that they are usually - // nested within another function, and (at least with how the C AST is currently - // exported) they have an end location with line and column zero. Fix this up before - // continuing to maintain the invariant that begin is not ordered after end. - if is_nested - && end_loc.line == 0 - && end_loc.column == 0 - && !(begin_loc.line == 0 && begin_loc.column == 0) - { - log::debug!("skipping nested decl with zero end line/col: {decl:?}"); - continue; - } - - // If the beginning is not ordered after the end, skip this decl and warn. - if begin_loc > end_loc { - log::warn!( - "backward source range for top-level decl; skipping. source ranges for \ - top-level decls may be incorrect.\ndecl: {decl:?}" - ); - continue; - } - - // End of the previous decl is the start of comments pertaining to the current one. - let earliest_begin_loc = if is_nested { begin_loc } else { prev_end_loc }; - - // Include only decls from the main file. - if self.c_decls_top.contains(decl_id) - && self.get_source_path(decl) == Some(&self.main_file) - { - // For multiple decls, e.g. `int a, b;`, `begin_loc` is shared, in which case it is - // earlier than `earliest_begin_loc` for decls after the first; to maintain their - // relative order we must either move `earliest_begin_loc` earlier or move - // `begin_loc` later. - // For now, we move `begin_loc` later, so that the range used by each variable from - // a multiple decl does not overlap the others. If other tooling would benefit more - // from maximal but overlapping ranges, we could go the other way. - let begin_loc = begin_loc.max(earliest_begin_loc); - let entry = CDeclSrcRange { - earliest_begin: earliest_begin_loc, - strict_begin: begin_loc, - end: end_loc, - }; - name_loc_map.insert(*decl_id, entry); - } - if !is_nested { - prev_end_loc = end_loc; - } - } - name_loc_map - } - - pub fn iter_decls(&self) -> indexmap::map::Iter<'_, CDeclId, CDecl> { - self.c_decls.iter() - } - - pub fn iter_mut_decls(&mut self) -> indexmap::map::IterMut<'_, CDeclId, CDecl> { - self.c_decls.iter_mut() - } - - pub fn get_decl(&self, key: &CDeclId) -> Option<&CDecl> { - self.c_decls.get(key) - } - - pub fn is_null_expr(&self, expr_id: CExprId) -> bool { - use CExprKind::*; - match self[expr_id].kind { - ExplicitCast(_, _, CastKind::NullToPointer, _, _) - | ImplicitCast(_, _, CastKind::NullToPointer, _, _) => true, - - ExplicitCast(ty, e, CastKind::BitCast, _, _) - | ImplicitCast(ty, e, CastKind::BitCast, _, _) => { - self.resolve_type(ty.ctype).kind.is_pointer() && self.is_null_expr(e) - } - - _ => false, - } - } - - /// Predicate for struct, union, and enum declarations without - /// bodies. These forward declarations are suitable for use as - /// the targets of pointers - pub fn is_forward_declared_type(&self, typ: CTypeId) -> bool { - use CDeclKind::*; - || -> Option<()> { - let decl_id = self.resolve_type(typ).kind.as_underlying_decl()?; - matches!( - self[decl_id].kind, - Struct { fields: None, .. } - | Union { fields: None, .. } - | Enum { - integral_type: None, - .. - } - ) - .then(|| ()) - }() - .is_some() - } - - /// Returns whether `typ` is a chain of typedefs that ends in `__builtin_va_list`, - /// thus naming the type clang uses to represent `va_list`s. - /// This works on all architectures, but does not work in situations where typedefs are - /// resolved/bypassed, such as with array-to-pointer decay. - pub fn is_builtin_va_list(&self, mut typ: CTypeId) -> bool { - loop { - // Skip over Elaborated types - let mut kind = &self.index(typ).kind; - - while let &CTypeKind::Elaborated(typ) = kind { - kind = &self.index(typ).kind; - } - - // TODO: Rust 1.65: use let-else - let decl = match kind { - &CTypeKind::Typedef(decl) => decl, - _ => return false, - }; - let (name, qtyp) = match &self.index(decl).kind { - &CDeclKind::Typedef { ref name, typ, .. } => (name, typ), - _ => panic!("Typedef decl did not point to a typedef"), - }; - - if name == "__builtin_va_list" { - return true; - } - - typ = qtyp.ctype; - } - } - - /// Returns whether `typ` is the architecture-specific type used for `va_list`. - /// Returns `false` for architectures where `va_list` is a generic pointer type. - pub fn is_va_list_struct(&self, typ: CTypeId) -> bool { - use BuiltinVaListKind::*; - - match self.va_list_kind { - // No special identification is possible with generic types. - CharPtrBuiltinVaList | VoidPtrBuiltinVaList => false, - - // ARM32: - // typedef struct __va_list { - // void *__ap; - // } __builtin_va_list; - - // ARM64: - // typedef struct __va_list { - // void *__stack; - // void *__gr_top; - // void *__vr_top; - // int __gr_offs; - // int __vr_offs; - // } __builtin_va_list; - AAPCSABIBuiltinVaList | AArch64ABIBuiltinVaList => { - // TODO: Rust 1.65: use let-else - let decl = match self.resolve_type(typ).kind { - CTypeKind::Struct(decl) => decl, - _ => return false, - }; - let name = match &self[decl].kind { - CDeclKind::Struct { - name: Some(name), .. - } => name, - _ => return false, - }; - - name == "__va_list" - } - - // X86-64: - // typedef struct __va_list_tag { - // unsigned int gp_offset; - // unsigned int fp_offset; - // void *overflow_arg_area; - // void *reg_save_area; - // } __builtin_va_list[1]; - - // Power: - // typedef struct __va_list_tag { - // unsigned char gpr; - // unsigned char fpr; - // unsigned short reserved; - // char *overflow_arg_area; - // char *reg_save_area; - // } __builtin_va_list[1]; - X86_64ABIBuiltinVaList | PowerABIBuiltinVaList => { - // TODO: Rust 1.65: use let-else - let inner = match self.resolve_type(typ).kind { - CTypeKind::ConstantArray(inner, 1) => inner, - // Account for array-to-pointer decay in function parameters. - CTypeKind::Pointer(CQualTypeId { ctype: inner, .. }) => inner, - _ => return false, - }; - let decl = match self.resolve_type(inner).kind { - CTypeKind::Struct(decl) => decl, - _ => return false, - }; - let name = match &self[decl].kind { - CDeclKind::Struct { - name: Some(name), .. - } => name, - _ => return false, - }; - - name == "__va_list_tag" - } - - kind => unimplemented!("va_list type {:?} not yet implemented", kind), - } - } - - /// Returns whether `typ` is a C `va_list`. - pub fn is_va_list(&self, typ: CTypeId) -> bool { - self.is_builtin_va_list(typ) || self.is_va_list_struct(typ) - } - - /// Predicate for function pointers - pub fn is_function_pointer(&self, typ: CTypeId) -> bool { - let resolved_ctype = self.resolve_type(typ); - use CTypeKind::*; - if let Pointer(p) = resolved_ctype.kind { - matches!(self.resolve_type(p.ctype).kind, Function { .. }) - } else { - false - } - } - - /// Returns the length of an array type, or panics. - pub fn array_len(&self, typ: CTypeId) -> usize { - match self.resolve_type(typ).kind { - CTypeKind::ConstantArray(_, len) => len, - ref kind => panic!("CTypeId {typ:?} is {kind:?}, not a ConstantArray"), - } - } - - /// Can the given field decl be a flexible array member? - pub fn maybe_flexible_array(&self, typ: CTypeId) -> bool { - let field_ty = self.resolve_type(typ); - use CTypeKind::*; - matches!(field_ty.kind, IncompleteArray(_) | ConstantArray(_, 0 | 1)) - } - - pub fn get_pointee_qual_type(&self, typ: CTypeId) -> Option { - let resolved_ctype = self.resolve_type(typ); - if let CTypeKind::Pointer(p) = resolved_ctype.kind { - Some(p) - } else { - None - } - } - - /// Returns the expression inside any number of nested parentheses. - pub fn resolve_parens(&self, mut expr_id: CExprId) -> CExprId { - while let CExprKind::Paren(_, subexpr) = self.index(expr_id).kind { - expr_id = subexpr; - } - - expr_id - } - - /// Returns the expression inside an `__extension__` operator. - pub fn resolve_extension(&self, expr_id: CExprId) -> CExprId { - if let CExprKind::Unary(_, UnOp::Extension, subexpr, _) = self.index(expr_id).kind { - subexpr - } else { - expr_id - } - } - - /// Unwraps a predefined expression, if there is one. - pub fn unwrap_predefined_ident(&self, mut expr_id: CExprId) -> CExprId { - expr_id = self.resolve_extension(self.resolve_parens(expr_id)); - - if let CExprKind::Predefined(_, subexpr) = self.index(expr_id).kind { - subexpr - } else { - expr_id - } - } - - /// Unwraps the underlying expression beneath any casts. - pub fn unwrap_cast_expr(&self, mut expr_id: CExprId) -> CExprId { - while let CExprKind::Paren(_, subexpr) - | CExprKind::ImplicitCast(_, subexpr, _, _, _) - | CExprKind::ExplicitCast(_, subexpr, _, _, _) = self.index(expr_id).kind - { - expr_id = subexpr; - } - - expr_id - } - - /// Unwraps the underlying expression beneath any implicit casts. - pub fn unwrap_implicit_cast_expr(&self, mut expr_id: CExprId) -> CExprId { - while let CExprKind::ImplicitCast(_, subexpr, _, _, _) = self.index(expr_id).kind { - expr_id = subexpr; - } - - expr_id - } - - /// Resolve true expression type, iterating through any casts and variable - /// references. - pub fn resolve_expr_type_id(&self, expr_id: CExprId) -> Option<(CExprId, CTypeId)> { - let expr = &self.index(expr_id).kind; - let mut ty = expr.get_type(); - use CExprKind::*; - match expr { - ImplicitCast(_, subexpr, _, _, _) - | ExplicitCast(_, subexpr, _, _, _) - | Paren(_, subexpr) => { - return self.resolve_expr_type_id(*subexpr); - } - DeclRef(_, decl_id, _) => { - let decl = self.index(*decl_id); - use CDeclKind::*; - match decl.kind { - Function { typ, .. } => { - ty = Some(self.resolve_type_id(typ)); - } - Variable { typ, .. } | Typedef { typ, .. } => { - ty = Some(self.resolve_type_id(typ.ctype)); - } - _ => {} - } - } - _ => {} - } - ty.map(|ty| (expr_id, ty)) - } - - pub fn type_for_kind(&self, kind: &CTypeKind) -> Option { - self.c_types - .iter() - .find_map(|(id, k)| if kind == &k.kind { Some(*id) } else { None }) - } - - pub fn resolve_type_id(&self, typ: CTypeId) -> CTypeId { - use CTypeKind::*; - let ty = match self.index(typ).kind { - Attributed(ty, _) => ty.ctype, - Elaborated(ty) => ty, - Decayed(ty) => ty, - TypeOf(ty) => ty, - Paren(ty) => ty, - Typedef(decl) => match self.index(decl).kind { - CDeclKind::Typedef { typ: ty, .. } => ty.ctype, - _ => panic!("Typedef decl did not point to a typedef"), - }, - _ => return typ, - }; - self.resolve_type_id(ty) - } - - pub fn resolve_type(&self, typ: CTypeId) -> &CType { - let resolved_typ_id = self.resolve_type_id(typ); - self.index(resolved_typ_id) - } - - /// Extract decl of referenced function. - /// Looks for ImplicitCast(FunctionToPointerDecay, DeclRef(function_decl)) - pub fn fn_declref_decl(&self, func_expr: CExprId) -> Option<&CDeclKind> { - use CastKind::FunctionToPointerDecay; - if let CExprKind::ImplicitCast(_, fexp, FunctionToPointerDecay, _, _) = self[func_expr].kind - { - if let CExprKind::DeclRef(_ty, decl_id, _rv) = &self[fexp].kind { - let decl = &self.index(*decl_id).kind; - assert!(matches!(decl, CDeclKind::Function { .. })); - return Some(decl); - } - } - None - } - - /// Return the list of types for a list of declared function parameters. - /// - /// Returns `None` if one of the parameters is not a `CDeclKind::Variable`, e.g. if it was not a - /// function parameter but actually some other kind of declaration. - pub fn tys_of_params(&self, parameters: &[CDeclId]) -> Option> { - parameters - .iter() - .map(|p| match self.index(*p).kind { - CDeclKind::Variable { typ, .. } => Some(CQualTypeId::new(typ.ctype)), - _ => None, - }) - .collect() - } - - /// Return the most precise possible CTypeKind for the given function declaration. - /// Specifically, ensures that arguments' types are not resolved to underlying types if they were - /// declared as typedefs, but returned as those typedefs. - /// - /// The passed CDeclId must refer to a function declaration. - pub fn fn_decl_ty_with_declared_args(&self, func_decl: &CDeclKind) -> CTypeKind { - if let CDeclKind::Function { - typ, parameters, .. - } = func_decl - { - let typ = self.resolve_type_id(*typ); - let decl_arg_tys = self.tys_of_params(parameters).unwrap(); - let typ_kind = &self[typ].kind; - if let &CTypeKind::Function(ret, ref _arg_tys, a, b, c) = typ_kind { - return CTypeKind::Function(ret, decl_arg_tys, a, b, c); - } - panic!("expected {typ:?} to be CTypeKind::Function, but it was {typ_kind:?}") - } - panic!("expected a CDeclKind::Function, but passed {func_decl:?}") - } - - /// Return the id of the most precise possible type for the function referenced by the given - /// expression, if any. - pub fn fn_declref_ty_with_declared_args(&self, func_expr: CExprId) -> Option { - if let Some(func_decl @ CDeclKind::Function { .. }) = self.fn_declref_decl(func_expr) { - let kind_with_declared_args = self.fn_decl_ty_with_declared_args(func_decl); - let specific_typ = self - .type_for_kind(&kind_with_declared_args) - .unwrap_or_else(|| panic!("no type for kind {kind_with_declared_args:?}")); - return Some(CQualTypeId::new(specific_typ)); - } - None - } - - /// Pessimistically try to check if an expression has side effects. If it does, or we can't tell - /// that it doesn't, return `false`. - pub fn is_expr_pure(&self, expr: CExprId) -> bool { - use CExprKind::*; - let pure = |expr| self.is_expr_pure(expr); - match self.index(expr).kind { - BadExpr | - ShuffleVector(..) | - ConvertVector(..) | - Call(..) | - Unary(_, UnOp::PreIncrement, _, _) | - Unary(_, UnOp::PostIncrement, _, _) | - Unary(_, UnOp::PreDecrement, _, _) | - Unary(_, UnOp::PostDecrement, _, _) | - Binary(_, BinOp::Assign, _, _, _, _) | - InitList { .. } | - ImplicitValueInit { .. } | - Predefined(..) | - Statements(..) | // TODO: more precision - VAArg(..) | - Atomic{..} => false, - - Literal(_, _) | - DeclRef(_, _, _) | - UnaryType(_, _, _, _) | - OffsetOf(..) | - ConstantExpr(..) => true, - - DesignatedInitExpr(_,_,e) | - ImplicitCast(_, e, _, _, _) | - ExplicitCast(_, e, _, _, _) | - Member(_, e, _, _, _) | - Paren(_, e) | - CompoundLiteral(_, e) | - Unary(_, _, e, _) => pure(e), - - Binary(_, op, _, _, _, _) if op.underlying_assignment().is_some() => false, - Binary(_, _, lhs, rhs, _, _) => pure(lhs) && pure(rhs), - - ArraySubscript(_, lhs, rhs, _) => pure(lhs) && pure(rhs), - Conditional(_, c, lhs, rhs) => pure(c) && pure(lhs) && pure(rhs), - BinaryConditional(_, c, rhs) => pure(c) && pure(rhs), - Choose(_, c, lhs, rhs, _) => pure(c) && pure(lhs) && pure(rhs), - } - } - - /// Pessimistically try to check if an expression doesn't return. - /// If it does, or we can't tell that it doesn't, return `false`. - pub fn expr_diverges(&self, expr_id: CExprId) -> bool { - let func_id = match self.index(expr_id).kind { - CExprKind::Call(_, func_id, _) => func_id, - _ => return false, - }; - - let type_id = match self[func_id].kind.get_type() { - None => return false, - Some(t) => t, - }; - let pointed_id = match self.index(type_id).kind { - CTypeKind::Pointer(pointer_qualtype) => pointer_qualtype.ctype, - _ => return false, - }; - - match self.index(pointed_id).kind { - CTypeKind::Function(_, _, _, no_return, _) => no_return, - _ => false, - } - } - - /// Pessimistically try to check if an expression is `const`. - /// If it's not, or we can't tell if it is, return `false`. - /// - /// This should be a top-down, pessimistic/conservative analysis. - pub fn is_const_expr(&self, expr: CExprId) -> bool { - let is_const = |expr| self.is_const_expr(expr); - - use CExprKind::*; - match self[expr].kind { - // A literal is always `const`. - Literal(_, _) => true, - // Unary ops should be `const`. - // TODO handle `f128` or use the primitive type. - Unary(_, _, expr, _) => is_const(expr), - // Not sure what a `None` `CExprId` means here - // or how to detect a `sizeof` of a VLA, which is non-`const`, - // although it seems we don't handle `sizeof(VLAs)` - // correctly in macros elsewhere already. - UnaryType(_, _, expr, _) => expr.map_or(true, is_const), - // Not sure what a `OffsetOfKind::Variable` means. - OffsetOf(_, _) => true, - // `ptr::offset` (ptr `BinOp::Add`) was `const` stabilized in `1.61.0`. - // `ptr::offset_from` (ptr `BinOp::Subtract`) was `const` stabilized in `1.65.0`. - // TODO `f128` is not yet handled, as we should eventually - // switch to the (currently unstable) `f128` primitive type (#1262). - Binary(_, _, lhs, rhs, _, _) => is_const(lhs) && is_const(rhs), - // `as` casts are always `const`. - ImplicitCast(_, expr, _, _, _) => is_const(expr), - // `as` casts are always `const`. - // TODO This is `const`, although there's a bug #853. - ExplicitCast(_, expr, _, _, _) => is_const(expr), - // This is used in `const` locations like `match` patterns and array lengths, so it must be `const`. - ConstantExpr(_, _, _) => true, - // A reference in an already otherwise `const` context should be `const` itself. - DeclRef(_, _, _) => true, - Call(_, fn_expr, ref args) => { - let is_const_fn = false; // TODO detect which `fn`s are `const`. - is_const(fn_expr) && args.iter().copied().all(is_const) && is_const_fn - } - Member(_, expr, _, _, _) => is_const(expr), - ArraySubscript(_, array, index, _) => is_const(array) && is_const(index), - Conditional(_, cond, if_true, if_false) => { - is_const(cond) && is_const(if_true) && is_const(if_false) - } - BinaryConditional(_, cond, if_false) => is_const(cond) && is_const(if_false), - InitList(_, ref inits, _, _) => inits.iter().copied().all(is_const), - ImplicitValueInit(_) => true, - Paren(_, expr) => is_const(expr), - CompoundLiteral(_, expr) => is_const(expr), - Predefined(_, expr) => is_const(expr), - Statements(_, stmt) => self.is_const_stmt(stmt), - VAArg(_, expr) => is_const(expr), - // SIMD is not yet `const` in Rust. - ShuffleVector(_, _) | ConvertVector(_, _) => false, - DesignatedInitExpr(_, _, expr) => is_const(expr), - Choose(_, cond, if_true, if_false, _) => { - is_const(cond) && is_const(if_true) && is_const(if_false) - } - // Atomics are not yet `const` in Rust. - Atomic { .. } => false, - BadExpr => false, - } - } - - pub fn is_const_stmt(&self, stmt: CStmtId) -> bool { - let is_const = |stmt| self.is_const_stmt(stmt); - let is_const_expr = |expr| self.is_const_expr(expr); - - use CStmtKind::*; - match self[stmt].kind { - Case(expr, stmt, _const_expr) => is_const_expr(expr) && is_const(stmt), - Default(stmt) => is_const(stmt), - Compound(ref stmts) => stmts.iter().copied().all(is_const), - Expr(expr) => is_const_expr(expr), - Empty => true, - If { - scrutinee, - true_variant, - false_variant, - } => { - is_const_expr(scrutinee) - && is_const(true_variant) - && false_variant.map_or(true, is_const) - } - Switch { scrutinee, body } => is_const_expr(scrutinee) && is_const(body), - While { condition, body } => is_const_expr(condition) && is_const(body), - DoWhile { body, condition } => is_const(body) && is_const_expr(condition), - ForLoop { - init, - condition, - increment, - body, - } => { - init.map_or(true, is_const) - && condition.map_or(true, is_const_expr) - && increment.map_or(true, is_const_expr) - && is_const(body) - } - Break => true, - Continue => true, - Return(expr) => expr.map_or(true, is_const_expr), - Decls(ref _decls) => true, - Asm { .. } => false, - Attributed { - attributes: _, - substatement, - } => is_const(substatement), - // `goto`s are tricky, because they can be non-local - // and jump out of the context of the macro. - // A `goto` and its labels are `const` if the whole state machine - // we compile to has all `const` statements, - // but determining what that is exactly is trickier, - // and might depend on the context in which the macro is used. - // This is probably fairly uncommon, so we just assume it's not `const` for now. - // Note that in C, labels are for `goto`s. - // There are no labeled `break`s and `continue`s. - Label(_stmt) => false, - Goto(_label) => false, - } - } - pub fn prune_unwanted_decls(&mut self, want_unused_functions: bool) { // Starting from a set of root declarations, walk each one to find declarations it // depends on. Then walk each of those, recursively. @@ -1091,7 +436,9 @@ impl TypedAstContext { if let CDeclKind::EnumConstant { .. } = self.c_decls[&decl_id].kind { // Special case for enums. The enum constant is used, so the whole // enum is also used. - let parent_id = self.parents[&decl_id]; + let parent_id = self + .parent_with_type(decl_id) + .expect("Enum constant does not have a parent Enum"); if wanted.insert(parent_id) { to_walk.push(parent_id); } @@ -1120,111 +467,6 @@ impl TypedAstContext { self.c_decls_top.retain(|x| wanted.contains(x)); } - /// Bubble types of unary and binary operators up from their args into the expression type. - /// - /// In Clang 15 and below, the Clang AST resolves typedefs in the expression type of unary and - /// binary expressions. For example, a BinaryExpr node adding two `size_t` expressions will be - /// given an `unsigned long` type rather than the `size_t` typedef type. This behavior changed - /// in Clang 16. This method adjusts AST node types to match those produced by Clang 16 and - /// newer; on these later Clang versions, it should have no effect. - /// - /// This pass is necessary because we reify some typedef types (such as `size_t`) into their own - /// distinct Rust types. As such, we need to make sure we know the exact type to generate when - /// we translate an expr, not just its resolved type (looking through typedefs). - pub fn bubble_expr_types(&mut self) { - struct BubbleExprTypes<'a> { - ast_context: &'a mut TypedAstContext, - } - - impl<'a> NodeVisitor for BubbleExprTypes<'a> { - fn children(&mut self, id: SomeId) -> Vec { - immediate_children_all_types(self.ast_context, id) - } - - fn post(&mut self, id: SomeId) { - let e = match id { - SomeId::Expr(e) => e, - _ => return, - }; - - let new_ty = match self.ast_context.c_exprs[&e].kind { - CExprKind::Conditional(_ty, _cond, lhs, rhs) => { - let lhs_type_id = - self.ast_context.c_exprs[&lhs].kind.get_qual_type().unwrap(); - let rhs_type_id = - self.ast_context.c_exprs[&rhs].kind.get_qual_type().unwrap(); - - let lhs_resolved_ty = self.ast_context.resolve_type(lhs_type_id.ctype); - let rhs_resolved_ty = self.ast_context.resolve_type(rhs_type_id.ctype); - - if CTypeKind::PULLBACK_KINDS.contains(&lhs_resolved_ty.kind) { - Some(lhs_type_id) - } else if CTypeKind::PULLBACK_KINDS.contains(&rhs_resolved_ty.kind) { - Some(rhs_type_id) - } else { - None - } - } - CExprKind::Binary(_ty, op, lhs, rhs, _, _) => { - let rhs_type_id = - self.ast_context.c_exprs[&rhs].kind.get_qual_type().unwrap(); - let lhs_kind = &self.ast_context.c_exprs[&lhs].kind; - let lhs_type_id = lhs_kind.get_qual_type().unwrap(); - - let lhs_resolved_ty = self.ast_context.resolve_type(lhs_type_id.ctype); - let rhs_resolved_ty = self.ast_context.resolve_type(rhs_type_id.ctype); - - let neither_ptr = !lhs_resolved_ty.kind.is_pointer() - && !rhs_resolved_ty.kind.is_pointer(); - - if op.all_types_same() && neither_ptr { - if CTypeKind::PULLBACK_KINDS.contains(&lhs_resolved_ty.kind) { - Some(lhs_type_id) - } else { - Some(rhs_type_id) - } - } else if op == BinOp::ShiftLeft || op == BinOp::ShiftRight { - Some(lhs_type_id) - } else { - return; - } - } - CExprKind::Unary(_ty, op, e, _idk) => op.expected_result_type( - self.ast_context, - self.ast_context.c_exprs[&e].kind.get_qual_type().unwrap(), - ), - CExprKind::Paren(_ty, e) => self.ast_context.c_exprs[&e].kind.get_qual_type(), - CExprKind::UnaryType(_, op, _, _) => { - // All of these `UnTypeOp`s should return `size_t`. - let kind = match op { - UnTypeOp::SizeOf => CTypeKind::Size, - UnTypeOp::AlignOf => CTypeKind::Size, - UnTypeOp::PreferredAlignOf => CTypeKind::Size, - }; - let ty = self - .ast_context - .type_for_kind(&kind) - .expect("CTypeKind::Size should be size_t"); - Some(CQualTypeId::new(ty)) - } - _ => return, - }; - let ty = self - .ast_context - .c_exprs - .get_mut(&e) - .and_then(|e| e.kind.get_qual_type_mut()); - if let (Some(ty), Some(new_ty)) = (ty, new_ty) { - *ty = new_ty; - }; - } - } - - for decl in self.c_decls_top.clone() { - BubbleExprTypes { ast_context: self }.visit_tree(SomeId::Decl(decl)); - } - } - /// Sort the top-level declarations by file and source location /// so that we preserve the ordering of all declarations in each file. /// This preserves the order when we emit the converted declarations. @@ -1233,43 +475,6 @@ impl TypedAstContext { decls_top.sort_unstable_by_key(|&decl| self.cmp_located_include(self.index(decl))); self.c_decls_top = decls_top; } - - pub fn has_inner_struct_decl(&self, decl_id: CDeclId) -> bool { - matches!( - self.index(decl_id).kind, - CDeclKind::Struct { - manual_alignment: Some(_), - .. - } - ) - } - - pub fn is_packed_struct_decl(&self, decl_id: CDeclId) -> bool { - use CDeclKind::*; - matches!( - self.index(decl_id).kind, - Struct { - is_packed: true, - .. - } | Struct { - max_field_alignment: Some(_), - .. - } - ) - } - - pub fn is_aligned_struct_type(&self, typ: CTypeId) -> bool { - if let Some(decl_id) = self.resolve_type(typ).kind.as_underlying_decl() { - if let CDeclKind::Struct { - manual_alignment: Some(_), - .. - } = self.index(decl_id).kind - { - return true; - } - } - false - } } impl CommentContext { @@ -1382,1212 +587,6 @@ impl CommentContext { } } -impl Index for TypedAstContext { - type Output = CType; - - fn index(&self, index: CTypeId) -> &CType { - match self.c_types.get(&index) { - None => panic!("Could not find {:?} in TypedAstContext", index), - Some(ty) => ty, - } - } -} - -impl Index for TypedAstContext { - type Output = CExpr; - fn index(&self, index: CExprId) -> &CExpr { - static BADEXPR: CExpr = Located { - loc: None, - kind: CExprKind::BadExpr, - }; - match self.c_exprs.get(&index) { - None => &BADEXPR, // panic!("Could not find {:?} in TypedAstContext", index), - Some(e) => { - // Transparently index through Paren expressions - if let CExprKind::Paren(_, subexpr) = e.kind { - self.index(subexpr) - } else { - e - } - } - } - } -} - -impl Index for TypedAstContext { - type Output = CDecl; - - fn index(&self, index: CDeclId) -> &CDecl { - match self.c_decls.get(&index) { - None => panic!("Could not find {:?} in TypedAstContext", index), - Some(ty) => ty, - } - } -} - -impl Index for TypedAstContext { - type Output = CStmt; - - fn index(&self, index: CStmtId) -> &CStmt { - match self.c_stmts.get(&index) { - None => panic!("Could not find {:?} in TypedAstContext", index), - Some(ty) => ty, - } - } -} - -/// All of our AST types should have location information bundled with them -pub type CDecl = Located; -pub type CStmt = Located; -pub type CExpr = Located; -pub type CType = Located; - -#[derive(Debug, Clone)] -pub enum CDeclKind { - // http://clang.llvm.org/doxygen/classclang_1_1FunctionDecl.html - Function { - is_global: bool, - is_inline: bool, - is_implicit: bool, - is_extern: bool, - is_inline_externally_visible: bool, - typ: CFuncTypeId, - name: String, - parameters: Vec, - body: Option, - attrs: IndexSet, - }, - - // http://clang.llvm.org/doxygen/classclang_1_1VarDecl.html - Variable { - has_static_duration: bool, - has_thread_duration: bool, - is_externally_visible: bool, - is_defn: bool, - ident: String, - initializer: Option, - typ: CQualTypeId, - attrs: IndexSet, - }, - - // Enum (http://clang.llvm.org/doxygen/classclang_1_1EnumDecl.html) - Enum { - name: Option, - variants: Vec, - integral_type: Option, - }, - - EnumConstant { - name: String, - value: ConstIntExpr, - }, - - // Typedef - Typedef { - name: String, - typ: CQualTypeId, - is_implicit: bool, - target_dependent_macro: Option, - }, - - // Struct - Struct { - name: Option, - fields: Option>, - is_packed: bool, - manual_alignment: Option, - max_field_alignment: Option, - platform_byte_size: u64, - platform_alignment: u64, - }, - - // Union - Union { - name: Option, - fields: Option>, - is_packed: bool, - }, - - // Field - Field { - name: String, - typ: CQualTypeId, - bitfield_width: Option, - platform_bit_offset: u64, - platform_type_bitwidth: u64, - }, - - MacroObject { - name: String, - // replacements: Vec, - }, - - MacroFunction { - name: String, - // replacements: Vec, - }, - - NonCanonicalDecl { - canonical_decl: CDeclId, - }, - - StaticAssert { - assert_expr: CExprId, - message: Option, - }, -} - -impl CDeclKind { - pub fn get_name(&self) -> Option<&String> { - use CDeclKind::*; - Some(match self { - Function { name: i, .. } => i, - Variable { ident: i, .. } => i, - Typedef { name: i, .. } => i, - EnumConstant { name: i, .. } => i, - Enum { name: Some(i), .. } => i, - Struct { name: Some(i), .. } => i, - Union { name: Some(i), .. } => i, - Field { name: i, .. } => i, - MacroObject { name, .. } => name, - _ => return None, - }) - } -} - -/// An OffsetOf Expr may or may not be a constant -#[derive(Debug, Clone)] -pub enum OffsetOfKind { - /// An Integer Constant Expr - Constant(u64), - /// Contains more information to generate - /// an offset_of! macro invocation - /// Struct Type, Field Decl Id, Index Expr - Variable(CQualTypeId, CDeclId, CExprId), -} - -/// Represents an expression in C (6.5 Expressions) -/// -/// This is modeled on Clang's APIs, so where documentation -/// is lacking here, look at Clang. -/// -/// We've kept a qualified type on every node since Clang has this information available, and since -/// the semantics of translations of certain constructs often depend on the type of the things they -/// are given. -/// -/// As per the C standard, qualifiers on types make sense only on lvalues. -#[derive(Debug, Clone)] -pub enum CExprKind { - /// Literal. - Literal(CQualTypeId, CLiteral), - - /// Unary operator. - Unary(CQualTypeId, UnOp, CExprId, LRValue), - - /// Unary type operator. - UnaryType(CQualTypeId, UnTypeOp, Option, CQualTypeId), - - /// `offsetof` expression. - OffsetOf(CQualTypeId, OffsetOfKind), - - /// Binary operator. - Binary( - CQualTypeId, - BinOp, - CExprId, - CExprId, - Option, - Option, - ), - - /// Implicit cast. - ImplicitCast(CQualTypeId, CExprId, CastKind, Option, LRValue), - - /// Explicit cast. - ExplicitCast(CQualTypeId, CExprId, CastKind, Option, LRValue), - - /// Constant context expression. - ConstantExpr(CQualTypeId, CExprId, Option), - - /// Reference to a decl (a variable, for instance). - // TODO: consider enforcing what types of declarations are allowed here - DeclRef(CQualTypeId, CDeclId, LRValue), - - /// Function call. - Call(CQualTypeId, CExprId, Vec), - - /// Member access. - Member(CQualTypeId, CExprId, CDeclId, MemberKind, LRValue), - - /// Array subscript access. - ArraySubscript(CQualTypeId, CExprId, CExprId, LRValue), - - /// Ternary conditional operator. - Conditional(CQualTypeId, CExprId, CExprId, CExprId), - - /// Binary conditional operator `?:` (GNU extension). - BinaryConditional(CQualTypeId, CExprId, CExprId), - - /// Initializer list. - /// - /// * type - /// * initializers - /// * union field - /// * syntactic form - InitList(CQualTypeId, Vec, Option, Option), - - /// Designated initializer. - ImplicitValueInit(CQualTypeId), - - /// Parenthesized expression. - /// - /// Ignored, but needed so we have a corresponding node. - Paren(CQualTypeId, CExprId), - - /// Compound literal. - CompoundLiteral(CQualTypeId, CExprId), - - /// Predefined expression. - Predefined(CQualTypeId, CExprId), - - /// Statement expression. - Statements(CQualTypeId, CStmtId), - - /// Variable argument list. - VAArg(CQualTypeId, CExprId), - - /// Unsupported shuffle vector operation. - ShuffleVector(CQualTypeId, Vec), - - /// Unsupported convert vector operation. - ConvertVector(CQualTypeId, Vec), - - /// From syntactic form of initializer list expressions. - DesignatedInitExpr(CQualTypeId, Vec, CExprId), - - /// GNU choose expression. - /// - /// * condition - /// * true expr - /// * false expr - /// * was condition true? - Choose(CQualTypeId, CExprId, CExprId, CExprId, bool), - - /// GNU/C11 atomic expression. - Atomic { - typ: CQualTypeId, - name: String, - ptr: CExprId, - order: CExprId, - val1: Option, - order_fail: Option, - val2: Option, - weak: Option, - }, - - BadExpr, -} - -#[derive(Copy, Debug, Clone)] -pub enum MemberKind { - Arrow, - Dot, -} - -impl CExprKind { - pub fn lrvalue(&self) -> LRValue { - match *self { - CExprKind::Unary(_, _, _, lrvalue) - | CExprKind::DeclRef(_, _, lrvalue) - | CExprKind::ImplicitCast(_, _, _, _, lrvalue) - | CExprKind::ExplicitCast(_, _, _, _, lrvalue) - | CExprKind::Member(_, _, _, _, lrvalue) - | CExprKind::ArraySubscript(_, _, _, lrvalue) => lrvalue, - _ => LRValue::RValue, - } - } - - pub fn get_qual_type(&self) -> Option { - self.clone().get_qual_type_mut().copied() - } - - pub fn get_qual_type_mut(&mut self) -> Option<&mut CQualTypeId> { - match self { - CExprKind::BadExpr => None, - CExprKind::Literal(ty, _) - | CExprKind::OffsetOf(ty, _) - | CExprKind::Unary(ty, _, _, _) - | CExprKind::UnaryType(ty, _, _, _) - | CExprKind::Binary(ty, _, _, _, _, _) - | CExprKind::ImplicitCast(ty, _, _, _, _) - | CExprKind::ExplicitCast(ty, _, _, _, _) - | CExprKind::DeclRef(ty, _, _) - | CExprKind::Call(ty, _, _) - | CExprKind::Member(ty, _, _, _, _) - | CExprKind::ArraySubscript(ty, _, _, _) - | CExprKind::Conditional(ty, _, _, _) - | CExprKind::BinaryConditional(ty, _, _) - | CExprKind::InitList(ty, _, _, _) - | CExprKind::ImplicitValueInit(ty) - | CExprKind::Paren(ty, _) - | CExprKind::CompoundLiteral(ty, _) - | CExprKind::Predefined(ty, _) - | CExprKind::Statements(ty, _) - | CExprKind::VAArg(ty, _) - | CExprKind::ShuffleVector(ty, _) - | CExprKind::ConvertVector(ty, _) - | CExprKind::DesignatedInitExpr(ty, _, _) - | CExprKind::ConstantExpr(ty, _, _) => Some(ty), - CExprKind::Choose(ty, _, _, _, _) | CExprKind::Atomic { typ: ty, .. } => Some(ty), - } - } - - pub fn get_type(&self) -> Option { - self.get_qual_type().map(|x| x.ctype) - } - - /// Try to determine the truthiness or falsiness of the expression. Return `None` if we can't - /// say anything. - pub fn get_bool(&self) -> Option { - match *self { - CExprKind::Literal(_, ref lit) => Some(lit.get_bool()), - _ => None, - } - } -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum CastKind { - BitCast, - LValueToRValue, - NoOp, - ToUnion, - ArrayToPointerDecay, - FunctionToPointerDecay, - NullToPointer, - IntegralToPointer, - PointerToIntegral, - ToVoid, - IntegralCast, - IntegralToBoolean, - IntegralToFloating, - FloatingToIntegral, - FloatingToBoolean, - BooleanToSignedIntegral, - PointerToBoolean, - FloatingCast, - FloatingRealToComplex, - FloatingComplexToReal, - FloatingComplexCast, - FloatingComplexToIntegralComplex, - IntegralRealToComplex, - IntegralComplexToReal, - IntegralComplexToBoolean, - IntegralComplexCast, - IntegralComplexToFloatingComplex, - BuiltinFnToFnPtr, - ConstCast, - VectorSplat, - AtomicToNonAtomic, - NonAtomicToAtomic, -} - -/// Represents a unary operator in C (6.5.3 Unary operators) and GNU C extensions -#[derive(Debug, Clone, Copy)] -pub enum UnOp { - AddressOf, // &x - Deref, // *x - Plus, // +x - PostIncrement, // x++ - PreIncrement, // ++x - Negate, // -x - PostDecrement, // x-- - PreDecrement, // --x - Complement, // ~x - Not, // !x - Real, // [GNU C] __real x - Imag, // [GNU C] __imag x - Extension, // [GNU C] __extension__ x - Coawait, // [C++ Coroutines] co_await x -} - -impl UnOp { - pub fn as_str(&self) -> &'static str { - use UnOp::*; - match self { - AddressOf => "&", - Deref => "*", - Plus => "+", - PreIncrement => "++", - PostIncrement => "++", - Negate => "-", - PreDecrement => "--", - PostDecrement => "--", - Complement => "~", - Not => "!", - Real => "__real", - Imag => "__imag", - Extension => "__extension__", - Coawait => "co_await", - } - } - - /// Obtain the expected type of a unary expression based on the operator and its argument type - pub fn expected_result_type( - &self, - ast_context: &TypedAstContext, - arg_type: CQualTypeId, - ) -> Option { - use UnOp::*; - let resolved_ty = ast_context.resolve_type(arg_type.ctype); - Some(match self { - // We could construct CTypeKind::Pointer here, but it is not guaranteed to have a - // corresponding `CTypeId` in the `TypedAstContext`, so bail out instead - AddressOf => return None, - Deref => { - if let CTypeKind::Pointer(inner) = resolved_ty.kind { - inner - } else { - panic!("dereferencing non-pointer type!") - } - } - Not => { - return ast_context - .type_for_kind(&CTypeKind::Int) - .map(CQualTypeId::new) - } - Real | Imag => { - if let CTypeKind::Complex(inner) = resolved_ty.kind { - CQualTypeId::new(inner) - } else { - panic!("__real or __imag applied to non-complex type!") - } - } - Coawait => panic!("trying to propagate co_await type"), - _ => CQualTypeId::new(arg_type.ctype), - }) - } -} - -impl Display for UnOp { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.as_str()) - } -} - -/// Represents a unary type operator in C -#[derive(Debug, Clone, Copy)] -pub enum UnTypeOp { - SizeOf, - AlignOf, - PreferredAlignOf, -} - -impl UnTypeOp { - pub fn as_str(&self) -> &'static str { - use UnTypeOp::*; - match self { - SizeOf => "sizeof", - AlignOf => "alignof", - PreferredAlignOf => "__alignof", - } - } -} - -impl Display for UnTypeOp { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.as_str()) - } -} - -impl UnOp { - /// Check is the operator is rendered before or after is operand. - pub fn is_prefix(&self) -> bool { - !matches!(*self, UnOp::PostIncrement | UnOp::PostDecrement) - } -} - -/// Represents a binary operator in C (6.5.5 Multiplicative operators - 6.5.14 Logical OR operator) -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum BinOp { - Multiply, // * - Divide, // / - Modulus, // % - Add, // + - Subtract, // - - ShiftLeft, // << - ShiftRight, // >> - Less, // < - Greater, // > - LessEqual, // <= - GreaterEqual, // >= - EqualEqual, // == - NotEqual, // != - BitAnd, // & - BitXor, // ^ - BitOr, // | - And, // && - Or, // || - - AssignAdd, // += - AssignSubtract, // -= - AssignMultiply, // *= - AssignDivide, // /= - AssignModulus, // %= - AssignBitXor, // ^= - AssignShiftLeft, // <<= - AssignShiftRight, // >>= - AssignBitOr, // |= - AssignBitAnd, // &= - - Assign, // = - Comma, // , -} - -impl BinOp { - pub fn as_str(&self) -> &'static str { - use BinOp::*; - match self { - Multiply => "*", - Divide => "/", - Modulus => "%", - Add => "+", - Subtract => "-", - ShiftLeft => "<<", - ShiftRight => ">>", - Less => "<", - Greater => ">", - LessEqual => "<=", - GreaterEqual => ">=", - EqualEqual => "==", - NotEqual => "!=", - BitAnd => "&", - BitXor => "^", - BitOr => "|", - And => "&&", - Or => "||", - - AssignAdd => "+=", - AssignSubtract => "-=", - AssignMultiply => "*=", - AssignDivide => "/=", - AssignModulus => "%=", - AssignBitXor => "^=", - AssignShiftLeft => "<<=", - AssignShiftRight => ">>=", - AssignBitOr => "|=", - AssignBitAnd => "&=", - - Assign => "=", - Comma => ", ", - } - } - - /// Does the rust equivalent of this operator have type (T, T) -> U? - #[rustfmt::skip] - pub fn input_types_same(&self) -> bool { - use BinOp::*; - self.all_types_same() || matches!(self, - Less | Greater | LessEqual | GreaterEqual | EqualEqual | NotEqual - | And | Or - | AssignAdd | AssignSubtract | AssignMultiply | AssignDivide | AssignModulus - | AssignBitXor | AssignShiftLeft | AssignShiftRight | AssignBitOr | AssignBitAnd - | Assign - ) - } - - /// Does the rust equivalent of this operator have type (T, T) -> T? - /// This ignores cases where one argument is a pointer and we translate to `.offset()`. - pub fn all_types_same(&self) -> bool { - use BinOp::*; - matches!( - self, - Multiply | Divide | Modulus | Add | Subtract | BitAnd | BitXor | BitOr - ) - } -} - -impl Display for BinOp { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.as_str()) - } -} - -impl BinOp { - /// Maps compound assignment operators to operator underlying them, and returns `None` for all - /// other operators. - /// - /// For example, `AssignAdd` maps to `Some(Add)` but `Add` maps to `None`. - pub fn underlying_assignment(&self) -> Option { - use BinOp::*; - Some(match *self { - AssignAdd => Add, - AssignSubtract => Subtract, - AssignMultiply => Multiply, - AssignDivide => Divide, - AssignModulus => Modulus, - AssignBitXor => BitXor, - AssignShiftLeft => ShiftLeft, - AssignShiftRight => ShiftRight, - AssignBitOr => BitOr, - AssignBitAnd => BitAnd, - _ => return None, - }) - } - - /// Determines whether or not this is an assignment op - pub fn is_assignment(&self) -> bool { - matches!(self, Self::Assign) || self.underlying_assignment().is_some() - } -} - -#[derive(Eq, PartialEq, Debug, Copy, Clone)] -pub enum IntBase { - Dec, - Hex, - Oct, -} - -#[derive(Debug, Clone)] -pub enum CLiteral { - Integer(u64, IntBase), // value and base - Character(u64), - Floating(f64, String), - String(Vec, u8), // Literal bytes and unit byte width -} - -impl CLiteral { - /// Determine the truthiness or falsiness of the literal. - pub fn get_bool(&self) -> bool { - use CLiteral::*; - match *self { - Integer(x, _) => x != 0u64, - Character(x) => x != 0u64, - Floating(x, _) => x != 0f64, - _ => true, - } - } -} - -/// Represents a constant integer expression as used in a case expression -#[derive(Debug, Clone, Copy, Eq, PartialEq)] -pub enum ConstIntExpr { - U(u64), - I(i64), -} - -/// Represents a statement in C (6.8 Statements) -/// -/// Reflects the types in -#[derive(Debug, Clone)] -pub enum CStmtKind { - // Labeled statements (6.8.1) - // - // All of these have a `CStmtId` to represent the substatement that comes after them - Label(CStmtId), - Case(CExprId, CStmtId, ConstIntExpr), - Default(CStmtId), - - // Compound statements (6.8.2) - Compound(Vec), - - // Expression and null statements (6.8.3) - Expr(CExprId), - Empty, - - // Selection statements (6.8.4) - If { - scrutinee: CExprId, - true_variant: CStmtId, - false_variant: Option, - }, - Switch { - scrutinee: CExprId, - body: CStmtId, - }, - - // Iteration statements (6.8.5) - While { - condition: CExprId, - body: CStmtId, - }, - DoWhile { - body: CStmtId, - condition: CExprId, - }, - ForLoop { - init: Option, - condition: Option, - increment: Option, - body: CStmtId, - }, - - // Jump statements (6.8.6) - Goto(CLabelId), - Break, - Continue, - Return(Option), - - // Declarations (variables, etc.) - Decls(Vec), - - // GCC inline assembly - Asm { - asm: String, - inputs: Vec, - outputs: Vec, - clobbers: Vec, - is_volatile: bool, - }, - - // Statements annotated with attributes. The substatement can be a NULL - // statement in case of __attribute__((__fallthrough__)) at the end of a - // case statement - Attributed { - attributes: Vec, - substatement: CStmtId, - }, -} - -#[derive(Clone, Debug)] -pub struct AsmOperand { - pub constraints: String, - pub expression: CExprId, -} - -/// Type qualifiers (6.7.3) -#[derive(Debug, Copy, Clone, Default, PartialEq, Eq)] -pub struct Qualifiers { - /// The `const` qualifier, which marks lvalues as non-assignable. - /// - /// We make use of `const` in only two places: - /// * Variable and function bindings (which matches up to Rust's `mut` or not bindings) - /// * The pointed type in pointers (which matches up to Rust's `*const`/`*mut`) - pub is_const: bool, - - pub is_restrict: bool, - - /// The `volatile` qualifier, which prevents the compiler from reordering accesses through such - /// qualified lvalues past other observable side effects (other accesses, or sequence points). - /// - /// The part here about not reordering (or changing in any way) access to something volatile - /// can be replicated in Rust via `std::ptr::read_volatile` and `std::ptr::write_volatile`. - /// Since Rust's execution model is still unclear, I am unsure that we get all of the guarantees - /// `volatile` needs, especially regarding reordering of other side-effects. - /// - /// To see where we use `volatile`, check the call-sites of `Translation::volatile_write` and - /// `Translation::volatile_read`. - pub is_volatile: bool, -} - -impl Qualifiers { - /// Aggregate qualifier information from two sources. - pub fn and(self, other: Qualifiers) -> Qualifiers { - Qualifiers { - is_const: self.is_const || other.is_const, - is_restrict: self.is_restrict || other.is_restrict, - is_volatile: self.is_volatile || other.is_volatile, - } - } -} - -/// Qualified type -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub struct CQualTypeId { - pub qualifiers: Qualifiers, - pub ctype: CTypeId, -} - -impl CQualTypeId { - pub fn new(ctype: CTypeId) -> Self { - Self { - qualifiers: Qualifiers::default(), - ctype, - } - } -} - -// TODO: these may be interesting, but I'm not sure if they fit here: -// -// * UnaryTransformType -// * AdjustedType - -/// Represents a type in C (6.2.5 Types) -/// -/// Reflects the types in -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum CTypeKind { - Void, - - // Boolean type (6.2.5.2) - Bool, - - // Character type (6.2.5.3) - Char, - - // Signed types (6.2.5.4) - SChar, - Short, - Int, - Long, - LongLong, - - // Unsigned types (6.2.5.6) (actually this also includes `_Bool`) - UChar, - UShort, - UInt, - ULong, - ULongLong, - - // Real floating types (6.2.5.10). Ex: `double` - Float, - Double, - LongDouble, - - // Clang specific types - Int128, - UInt128, - - Complex(CTypeId), - - // Pointer types (6.7.5.1) - Pointer(CQualTypeId), - - // C++ Reference - Reference(CQualTypeId), - - // Array types (6.7.5.2) - // - // A qualifier on an array type means the same thing as a qualifier on its element type. Since - // Clang tracks the qualifiers in both places, we choose to discard qualifiers on the element - // type. - // - // The size expression on a variable-length array is optional, it might be replaced with `*` - ConstantArray(CTypeId, usize), - IncompleteArray(CTypeId), - VariableArray(CTypeId, Option), - - // Type of type or expression (GCC extension) - TypeOf(CTypeId), - TypeOfExpr(CExprId), - - // Function type (6.7.5.3) - // - // Note a function taking no arguments should have one `void` argument. Functions without any - // arguments are in K&R format. - // Flags: is_variable_argument, is_noreturn, has prototype - Function(CQualTypeId, Vec, bool, bool, bool), - - // Type definition type (6.7.7) - Typedef(CTypedefId), - - // Represents a pointer type decayed from an array or function type. - Decayed(CTypeId), - Elaborated(CTypeId), - - // Type wrapped in parentheses - Paren(CTypeId), - - // Struct type - Struct(CRecordId), - - // Union type - Union(CRecordId), - - // Enum definition type - Enum(CEnumId), - - BuiltinFn, - - Attributed(CQualTypeId, Option), - - BlockPointer(CQualTypeId), - - Vector(CQualTypeId, usize), - - Half, - BFloat16, - - // ARM Scalable Vector Extension types - // TODO: represent all the individual types in AArch64SVEACLETypes.def - UnhandledSveType, - - Float128, - // Atomic types (6.7.2.4) - Atomic(CQualTypeId), - - // Rust sized types, pullback'd into C so that we can treat uint16_t, etc. as real types. - Int8, - Int16, - Int32, - Int64, - IntPtr, - UInt8, - UInt16, - UInt32, - UInt64, - UIntPtr, - IntMax, - UIntMax, - Size, - SSize, - PtrDiff, - WChar, -} - -impl CTypeKind { - pub const PULLBACK_KINDS: [CTypeKind; 16] = { - use CTypeKind::*; - [ - Int8, Int16, Int32, Int64, IntPtr, UInt8, UInt16, UInt32, UInt64, UIntPtr, IntMax, - UIntMax, Size, SSize, PtrDiff, WChar, - ] - }; - - pub fn as_str(&self) -> &'static str { - use CTypeKind::*; - match self { - Void => "void", - Bool => "_Bool", - Char => "char", - SChar => "signed char", - Short => "signed short", - Int => "int", - Long => "long", - LongLong => "long long", - UChar => "unsigned char", - UShort => "unsigned short", - UInt => "unsigned int", - ULong => "unsigned long", - ULongLong => "unsigned long long", - Float => "float", - Double => "double", - LongDouble => "long double", - Int128 => "__int128", - UInt128 => "unsigned __int128", - Half => "half", - BFloat16 => "bfloat16", - Float128 => "__float128", - - Int8 => "int8_t", - Int16 => "int16_t", - Int32 => "int32_t", - Int64 => "int64_t", - IntPtr => "intptr_t", - UInt8 => "uint8_t", - UInt16 => "uint16_t", - UInt32 => "uint32_t", - UInt64 => "uint64_t", - UIntPtr => "uintptr_t", - IntMax => "intmax_t", - UIntMax => "uintmax_t", - Size => "size_t", - SSize => "ssize_t", - PtrDiff => "ptrdiff_t", - WChar => "wchar_t", - - _ => unimplemented!("Printer::print_type({:?})", self), - } - } - - /// Whether `value` is guaranteed to be in this integer type's range. - /// Thus, the narrowest possible range is used. - /// - /// For example, for [`Self::Long`], [`i32`]'s range is used, - /// as on Linux and macOS (LP64), it's an [`i64`], - /// but on Windows (LLP64), it's only an [`i32`]. - /// - /// This should only be called on integer types. - /// Other types will return `false`. - pub fn guaranteed_integer_in_range(&self, value: u64) -> bool { - fn in_range>(value: u64) -> bool { - T::try_from(value).is_ok() - } - - use CTypeKind::*; - match *self { - Void => false, - - // Kind of an integer type, but would definitely need an explicit cast. - Bool => false, - - // Can be signed or unsigned, so choose the minimum range of each. - Char => (u8::MIN as u64..=i8::MAX as u64).contains(&value), - WChar => in_range::(value), - - // `int` is at least `i16` and `long` is at least `i32`. - SChar => in_range::(value), - Short => in_range::(value), - Int => in_range::(value), - Long => in_range::(value), - LongLong => in_range::(value), - - // `unsigned int` is at least `u16` and `unsigned long` is at least `u32`. - UChar => in_range::(value), - UShort => in_range::(value), - UInt => in_range::(value), - ULong => in_range::(value), - ULongLong => in_range::(value), - - Int8 => in_range::(value), - Int16 => in_range::(value), - Int32 => in_range::(value), - Int64 => in_range::(value), - Int128 => in_range::(value), - - UInt8 => in_range::(value), - UInt16 => in_range::(value), - UInt32 => in_range::(value), - UInt64 => in_range::(value), - UInt128 => in_range::(value), - - // There's no guarantee on pointer size, but `NULL` should work. - IntPtr => value == 0, - UIntPtr => value == 0, - - IntMax => in_range::(value), - UIntMax => in_range::(value), - - // `size_t` is at least a `u16`, and similar for `ssize_t` and `ptrdiff_t`. - Size => in_range::(value), - SSize => in_range::(value), - PtrDiff => in_range::(value), - - // Floats, see `Self::guaranteed_float_in_range`. - Float => false, - Double => false, - LongDouble => false, - Half => false, - BFloat16 => false, - Float128 => false, - - // Non-scalars. - Complex(_) => false, - Pointer(_) => false, - Reference(_) => false, - ConstantArray(_, _) => false, - IncompleteArray(_) => false, - VariableArray(_, _) => false, - TypeOf(_) => false, - TypeOfExpr(_) => false, - Function(_, _, _, _, _) => false, - Typedef(_) => false, - Decayed(_) => false, - Elaborated(_) => false, - Paren(_) => false, - Struct(_) => false, - Union(_) => false, - Enum(_) => false, - BuiltinFn => false, - Attributed(_, _) => false, - BlockPointer(_) => false, - Vector(_, _) => false, - UnhandledSveType => false, - Atomic(_) => false, - } - } - - /// See [`Self::guaranteed_integer_in_range`]. - /// This is the same, but for floats. - /// - /// This should only be called on float types. - /// Other types will return `false`. - pub fn guaranteed_float_in_range(&self, value: f64) -> bool { - fn in_range>(value: f64) -> bool { - T::try_from(value).is_ok() - } - - use CTypeKind::*; - match *self { - // `f32: TryFrom` is not implemented. - // C `float`s are not guaranteed to be `f32`, - // but Rust (namely `libc`) doesn't support any platform where this isn't the case. - Float => value >= f32::MIN as f64 && value <= f32::MAX as f64, - - // Similarly to `float`, `double` is not guaranteed to be `f64`, - // but `libc` doesn't support any platform where this isn't the case. - Double => in_range::(value), - - // `long double` (not `f128`) is only guaranteed to be at least as precise as a `double`. - LongDouble => in_range::(value), - - // All `f64`s are valid `f128`s. - Float128 => in_range::(value), - - // TODO Would like to depend on `half`. - Half => todo!("f16 range"), - BFloat16 => todo!("bf16 range"), - - Void => false, - Bool => false, - Char => false, - SChar => false, - Short => false, - Int => false, - Long => false, - LongLong => false, - UChar => false, - UShort => false, - UInt => false, - ULong => false, - ULongLong => false, - Int128 => false, - UInt128 => false, - Complex(_) => false, - Pointer(_) => false, - Reference(_) => false, - ConstantArray(_, _) => false, - IncompleteArray(_) => false, - VariableArray(_, _) => false, - TypeOf(_) => false, - TypeOfExpr(_) => false, - Function(_, _, _, _, _) => false, - Typedef(_) => false, - Decayed(_) => false, - Elaborated(_) => false, - Paren(_) => false, - Struct(_) => false, - Union(_) => false, - Enum(_) => false, - BuiltinFn => false, - Attributed(_, _) => false, - BlockPointer(_) => false, - Vector(_, _) => false, - UnhandledSveType => false, - Atomic(_) => false, - Int8 => false, - Int16 => false, - Int32 => false, - Int64 => false, - IntPtr => false, - UInt8 => false, - UInt16 => false, - UInt32 => false, - UInt64 => false, - UIntPtr => false, - IntMax => false, - UIntMax => false, - Size => false, - SSize => false, - PtrDiff => false, - WChar => false, - } - } -} - -impl Display for CTypeKind { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.as_str()) - } -} - -#[derive(Copy, Clone, Debug)] -pub enum Designator { - Index(u64), - Range(u64, u64), - Field(CFieldId), -} - /// Enumeration of supported attributes for Declarations #[derive(Clone, Debug, Eq, Hash, PartialEq)] pub enum Attribute { @@ -2614,166 +613,6 @@ pub enum Attribute { Fallthrough, } -impl CTypeKind { - pub fn is_pointer(&self) -> bool { - matches!(*self, Self::Pointer { .. }) - } - - pub fn is_bool(&self) -> bool { - matches!(*self, Self::Bool) - } - - pub fn is_enum(&self) -> bool { - matches!(*self, Self::Enum { .. }) - } - - pub fn is_integral_type(&self) -> bool { - self.is_unsigned_integral_type() || self.is_signed_integral_type() - } - - pub fn is_unsigned_integral_type(&self) -> bool { - use CTypeKind::*; - matches!( - self, - Bool | UChar - | UInt - | UShort - | ULong - | ULongLong - | UInt128 - | UInt8 - | UInt16 - | UInt32 - | UInt64 - | UIntPtr - | UIntMax - | Size - | WChar - ) - } - - pub fn is_signed_integral_type(&self) -> bool { - use CTypeKind::*; - // `Char` is true on the platforms we handle - matches!( - self, - Char | SChar - | Int - | Short - | Long - | LongLong - | Int128 - | Int8 - | Int16 - | Int32 - | Int64 - | IntPtr - | IntMax - | SSize - | PtrDiff - ) - } - - pub fn is_floating_type(&self) -> bool { - use CTypeKind::*; - matches!(self, Float | Double | LongDouble | Half | BFloat16) - } - - pub fn as_underlying_decl(&self) -> Option { - use CTypeKind::*; - match *self { - Struct(decl_id) | Union(decl_id) | Enum(decl_id) => Some(decl_id), - _ => None, - } - } - - pub fn as_decl_or_typedef(&self) -> Option { - use CTypeKind::*; - match *self { - Typedef(decl_id) | Struct(decl_id) | Union(decl_id) | Enum(decl_id) => Some(decl_id), - _ => None, - } - } - - pub fn is_vector(&self) -> bool { - matches!(self, Self::Vector { .. }) - } - - /// Choose the smaller, simpler of the two types if they are cast-compatible. - pub fn smaller_compatible_type(ty1: CTypeKind, ty2: CTypeKind) -> Option { - let int = Self::is_integral_type; - let float = Self::is_floating_type; - - use CTypeKind::*; - let ty = match (&ty1, &ty2) { - (ty, ty2) if ty == ty2 => ty1, - (Void, _) => ty2, - (Bool, ty) | (ty, Bool) if int(ty) => Bool, - - (Char, ty) | (ty, Char) if int(ty) => Char, - (SChar, ty) | (ty, SChar) if int(ty) => SChar, - (UChar, ty) | (ty, UChar) if int(ty) => UChar, - - (Short, ty) | (ty, Short) if int(ty) => Short, - (UShort, ty) | (ty, UShort) if int(ty) => UShort, - - (Int, ty) | (ty, Int) if int(ty) => Int, - (UInt, ty) | (ty, UInt) if int(ty) => UInt, - - (Float, ty) | (ty, Float) if float(ty) || int(ty) => Float, - - (Long, ty) | (ty, Long) if int(ty) => Long, - (ULong, ty) | (ty, ULong) if int(ty) => ULong, - - (Double, ty) | (ty, Double) if float(ty) || int(ty) => Double, - - (LongLong, ty) | (ty, LongLong) if int(ty) => LongLong, - (ULongLong, ty) | (ty, ULongLong) if int(ty) => ULongLong, - - (LongDouble, ty) | (ty, LongDouble) if float(ty) || int(ty) => LongDouble, - - (Int128, ty) | (ty, Int128) if int(ty) => Int128, - (UInt128, ty) | (ty, UInt128) if int(ty) => UInt128, - - // Integer to pointer conversion. We want to keep the integer and - // cast to a pointer at use. - (Pointer(_), ty) if int(ty) => ty2, - (ty, Pointer(_)) if int(ty) => ty1, - - // Array to pointer decay. We want to use the array and push the - // decay to the use of the value. - (Pointer(ptr_ty), ConstantArray(arr_ty, _)) - | (Pointer(ptr_ty), IncompleteArray(arr_ty)) - | (Pointer(ptr_ty), VariableArray(arr_ty, _)) - if ptr_ty.ctype == *arr_ty => - { - ty2 - } - (ConstantArray(arr_ty, _), Pointer(ptr_ty)) - | (IncompleteArray(arr_ty), Pointer(ptr_ty)) - | (VariableArray(arr_ty, _), Pointer(ptr_ty)) - if ptr_ty.ctype == *arr_ty => - { - ty1 - } - - _ => return None, - }; - Some(ty) - } - - /// Return the element type of a pointer or array - pub fn element_ty(&self) -> Option { - Some(match *self { - Self::Pointer(ty) => ty.ctype, - Self::ConstantArray(ty, _) => ty, - Self::IncompleteArray(ty) => ty, - Self::VariableArray(ty, _) => ty, - _ => return None, - }) - } -} - #[cfg(test)] mod tests { use std::cmp::Ordering; @@ -3039,3 +878,44 @@ c = {c} locs.sort_unstable_by_key(|&loc| ctx.cmp_loc_include(loc)); } } + +#[derive(Copy, Clone, Eq, PartialEq, Debug, Hash)] +pub enum SomeId { + Stmt(CStmtId), + Expr(CExprId), + Decl(CDeclId), + Type(CTypeId), +} + +macro_rules! from_some_id { + ( $field_type:ty, $con_name:ident, $proj_name:ident ) => { + impl From<$field_type> for SomeId { + fn from(a: $field_type) -> Self { + SomeId::$con_name(a) + } + } + impl TryFrom for $field_type { + type Error = (); + + fn try_from(id: SomeId) -> Result { + match id { + SomeId::$con_name(x) => Ok(x), + _ => Err(()), + } + } + } + impl SomeId { + pub fn $proj_name(self) -> Option<$field_type> { + match self { + SomeId::$con_name(x) => Some(x), + _ => None, + } + } + } + }; +} + +from_some_id!(CExprId, Expr, expr); +from_some_id!(CStmtId, Stmt, stmt); +from_some_id!(CDeclId, Decl, decl); +from_some_id!(CTypeId, Type, type_); diff --git a/c2rust-transpile/src/c_ast/print.rs b/c2rust-transpile/src/c_ast/print.rs index 1c2c23d2df..7f750527da 100644 --- a/c2rust-transpile/src/c_ast/print.rs +++ b/c2rust-transpile/src/c_ast/print.rs @@ -1,4 +1,8 @@ -use crate::c_ast::*; +use crate::c_ast::c_decl::{CDeclId, CDeclKind}; +use crate::c_ast::c_expr::{CBinOp, CExprId, CExprKind, CLiteral, CUnOp, MemberKind, OffsetOfKind}; +use crate::c_ast::c_stmt::{CStmtId, CStmtKind}; +use crate::c_ast::c_type::{CQualTypeId, CTypeId, CTypeKind, Qualifiers}; +use crate::c_ast::TypedAstContext; use std::io::{Result, Write}; pub struct Printer { @@ -287,11 +291,11 @@ impl Printer { Ok(()) } - pub fn print_unop(&mut self, op: &UnOp, _context: &TypedAstContext) -> Result<()> { + pub fn print_unop(&mut self, op: &CUnOp, _context: &TypedAstContext) -> Result<()> { self.writer.write_all(op.as_str().as_bytes()) } - pub fn print_binop(&mut self, op: &BinOp, _context: &TypedAstContext) -> Result<()> { + pub fn print_binop(&mut self, op: &CBinOp, _context: &TypedAstContext) -> Result<()> { self.writer.write_all(op.as_str().as_bytes()) } diff --git a/c2rust-transpile/src/cfg/mod.rs b/c2rust-transpile/src/cfg/mod.rs index 76549ee735..96d14753cc 100644 --- a/c2rust-transpile/src/cfg/mod.rs +++ b/c2rust-transpile/src/cfg/mod.rs @@ -15,8 +15,9 @@ //! - convert the `Vec>` back into a `Vec` //! -use crate::c_ast::iterators::{DFExpr, SomeId}; -use crate::c_ast::CLabelId; +use crate::c_ast::c_stmt::CLabelId; +use crate::c_ast::iterators::DFExpr; +use crate::c_ast::SomeId; use crate::diagnostics::TranslationResult; use crate::rust_ast::SpanExt; use c2rust_ast_printer::pprust; @@ -42,7 +43,11 @@ use serde::ser::{ }; use serde_json; -use crate::c_ast::*; +use crate::c_ast::c_decl::CDeclId; +use crate::c_ast::c_expr::{CExprId, CExprKind, CUnOp, ConstIntExpr}; +use crate::c_ast::c_stmt::{CStmtId, CStmtKind}; +use crate::c_ast::c_type::CQualTypeId; +use crate::c_ast::TypedAstContext; use crate::translator::*; use crate::with_stmts::WithStmts; use c2rust_ast_builder::mk; @@ -1783,7 +1788,7 @@ impl CfgBuilder { // This case typically happens in macros from system headers. // We simply inline the common statement at this point rather // than to try and create new control-flow blocks. - let blk_or_wip = if let CExprKind::Unary(_, UnOp::Extension, sube, _) = + let blk_or_wip = if let CExprKind::Unary(_, CUnOp::Extension, sube, _) = translator.ast_context[expr].kind { if let CExprKind::Statements(_, stmtid) = translator.ast_context[sube].kind { diff --git a/c2rust-transpile/src/convert_type.rs b/c2rust-transpile/src/convert_type.rs index 6cf037d587..5055a39855 100644 --- a/c2rust-transpile/src/convert_type.rs +++ b/c2rust-transpile/src/convert_type.rs @@ -1,5 +1,6 @@ -use crate::c_ast::CDeclId; -use crate::c_ast::*; +use crate::c_ast::c_decl::{CDeclId, CDeclKind, CFieldId, CParamId, CRecordId}; +use crate::c_ast::c_type::{CQualTypeId, CTypeId, CTypeKind}; +use crate::c_ast::TypedAstContext; use crate::diagnostics::TranslationResult; use crate::renamer::*; use crate::translator::variadic::mk_va_list_ty; diff --git a/c2rust-transpile/src/lib.rs b/c2rust-transpile/src/lib.rs index 17238a2e3c..1d59a5db10 100644 --- a/c2rust-transpile/src/lib.rs +++ b/c2rust-transpile/src/lib.rs @@ -30,8 +30,7 @@ use serde_derive::Serialize; pub use tempfile::TempDir; use which::which; -use crate::c_ast::Printer; -use crate::c_ast::*; +use crate::c_ast::{ConversionContext, Printer}; pub use crate::diagnostics::Diagnostic; use c2rust_ast_exporter as ast_exporter; diff --git a/c2rust-transpile/src/translator/comments.rs b/c2rust-transpile/src/translator/comments.rs index ed9e8ed4bb..86bb436e13 100644 --- a/c2rust-transpile/src/translator/comments.rs +++ b/c2rust-transpile/src/translator/comments.rs @@ -1,6 +1,7 @@ use super::Translation; -use crate::c_ast::iterators::{immediate_children_all_types, NodeVisitor, SomeId}; -use crate::c_ast::{CDeclId, CDeclKind, CommentContext, SrcLoc, TypedAstContext}; +use crate::c_ast::c_decl::{CDeclId, CDeclKind}; +use crate::c_ast::iterators::{immediate_children_all_types, NodeVisitor}; +use crate::c_ast::{CommentContext, SomeId, SrcLoc, TypedAstContext}; use crate::rust_ast::comment_store::CommentStore; use crate::rust_ast::{pos_to_span, SpanExt}; use log::debug; diff --git a/c2rust-transpile/src/translator/enums.rs b/c2rust-transpile/src/translator/enums.rs index afaa10164c..e40aa9b28f 100644 --- a/c2rust-transpile/src/translator/enums.rs +++ b/c2rust-transpile/src/translator/enums.rs @@ -2,14 +2,12 @@ use c2rust_ast_builder::mk; use proc_macro2::Span; use syn::Expr; -use crate::{ - c_ast, - diagnostics::TranslationResult, - translator::{signed_int_expr, ConvertedDecl, ExprContext, Translation}, - with_stmts::WithStmts, - CDeclKind, CEnumConstantId, CEnumId, CExprId, CExprKind, CLiteral, CQualTypeId, CTypeId, - CTypeKind, ConstIntExpr, -}; +use crate::c_ast::c_decl::{CDeclKind, CEnumConstantId, CEnumId}; +use crate::c_ast::c_expr::{CExprId, CExprKind, CLiteral, CUnOp, ConstIntExpr}; +use crate::c_ast::c_type::{CQualTypeId, CTypeId, CTypeKind}; +use crate::diagnostics::TranslationResult; +use crate::translator::{signed_int_expr, ConvertedDecl, ExprContext, Translation}; +use crate::with_stmts::WithStmts; impl<'c> Translation<'c> { pub fn convert_enum( @@ -40,7 +38,10 @@ impl<'c> Translation<'c> { .borrow_mut() .get(&enum_constant_id) .expect("Enum constant not named"); - let enum_id = self.ast_context.parents[&enum_constant_id]; + let enum_id = self + .ast_context + .parent_with_type(enum_constant_id) + .expect("Enum constant does not have a parent Enum"); let enum_name = self .type_converter .borrow() @@ -113,7 +114,7 @@ impl<'c> Translation<'c> { return Ok(self.enum_for_i64(enum_type_id, i as i64)); } - CExprKind::Unary(_, c_ast::UnOp::Negate, subexpr_id, _) => { + CExprKind::Unary(_, CUnOp::Negate, subexpr_id, _) => { if let &CExprKind::Literal(_, CLiteral::Integer(i, _)) = &self.ast_context[subexpr_id].kind { diff --git a/c2rust-transpile/src/translator/mod.rs b/c2rust-transpile/src/translator/mod.rs index 13190694a3..db8069098e 100644 --- a/c2rust-transpile/src/translator/mod.rs +++ b/c2rust-transpile/src/translator/mod.rs @@ -34,17 +34,26 @@ use crate::rust_ast::{pos_to_span, SpanExt}; use crate::translator::named_references::NamedReference; use crate::translator::variadic::{mk_va_list_copy, mk_va_list_ty}; use c2rust_ast_builder::{mk, properties::*, Builder}; +use c2rust_ast_exporter::clang_ast::SrcSpan; use c2rust_ast_printer::pprust; -use crate::c_ast::iterators::{DFExpr, SomeId}; +use crate::c_ast::c_decl::{CDecl, CDeclId, CDeclKind, CDeclSrcRange, CFieldId}; +use crate::c_ast::c_expr::{ + CBinOp, CExprId, CExprKind, CLiteral, CUnOp, CUnTypeOp, CastKind, ConstIntExpr, IntBase, + OffsetOfKind, +}; +use crate::c_ast::c_stmt::{AsmOperand, CStmtId, CStmtKind}; +use crate::c_ast::c_type::{CQualTypeId, CTypeId, CTypeKind}; +use crate::c_ast::iterators::DFExpr; +use crate::c_ast::{CommentContext, FileId, Located, SomeId, TypedAstContext}; use crate::cfg; use crate::convert_type::TypeConverter; use crate::renamer::Renamer; use crate::with_stmts::WithStmts; +use crate::TranslateMacros; use crate::{c_ast, format_translation_err}; -use crate::{c_ast::*, TranslateMacros}; use crate::{ExternCrate, TranspilerConfig}; -use c2rust_ast_exporter::clang_ast::LRValue; +use c2rust_ast_exporter::clang_ast::{LRValue, SrcLoc}; mod assembly; mod atomics; @@ -716,11 +725,6 @@ pub fn translate( t.ast_context .prune_unwanted_decls(tcfg.preserve_unused_functions); - // Normalize AST types between Clang < 16 and later versions. Ensures that - // binary and unary operators' expr types agree with their argument types - // in the presence of typedefs. - t.ast_context.bubble_expr_types(); - enum Name<'a> { Var(&'a str), Type(&'a str), @@ -1709,9 +1713,9 @@ impl<'c> Translation<'c> { expr_id: Option, qtype: CQualTypeId, ) -> bool { - use crate::c_ast::BinOp::{Add, Divide, Modulus, Multiply, Subtract}; - use crate::c_ast::CastKind::{IntegralToPointer, PointerToIntegral}; - use crate::c_ast::UnOp::{AddressOf, Negate}; + use crate::c_ast::c_expr::CBinOp::{Add, Divide, Modulus, Multiply, Subtract}; + use crate::c_ast::c_expr::CUnOp::{AddressOf, Negate}; + use crate::c_ast::c_expr::CastKind::{IntegralToPointer, PointerToIntegral}; let expr_id = match expr_id { Some(expr_id) => expr_id, @@ -2726,31 +2730,31 @@ impl<'c> Translation<'c> { }; match self.ast_context[cond_id].kind { - CExprKind::Binary(_, c_ast::BinOp::EqualEqual, null_expr, ptr, _, _) + CExprKind::Binary(_, CBinOp::EqualEqual, null_expr, ptr, _, _) if self.ast_context.is_null_expr(null_expr) => { null_pointer_case(ptr, target) } - CExprKind::Binary(_, c_ast::BinOp::EqualEqual, ptr, null_expr, _, _) + CExprKind::Binary(_, CBinOp::EqualEqual, ptr, null_expr, _, _) if self.ast_context.is_null_expr(null_expr) => { null_pointer_case(ptr, target) } - CExprKind::Binary(_, c_ast::BinOp::NotEqual, null_expr, ptr, _, _) + CExprKind::Binary(_, CBinOp::NotEqual, null_expr, ptr, _, _) if self.ast_context.is_null_expr(null_expr) => { null_pointer_case(ptr, !target) } - CExprKind::Binary(_, c_ast::BinOp::NotEqual, ptr, null_expr, _, _) + CExprKind::Binary(_, CBinOp::NotEqual, ptr, null_expr, _, _) if self.ast_context.is_null_expr(null_expr) => { null_pointer_case(ptr, !target) } - CExprKind::Unary(_, c_ast::UnOp::Not, subexpr_id, _) => { + CExprKind::Unary(_, CUnOp::Not, subexpr_id, _) => { self.convert_condition(ctx, !target, subexpr_id) } @@ -3053,8 +3057,7 @@ impl<'c> Translation<'c> { } // ref decayed ptrs generally need a type annotation - if let Some(CExprKind::Unary(_, c_ast::UnOp::AddressOf, _, _)) = - initializer_kind + if let Some(CExprKind::Unary(_, CUnOp::AddressOf, _, _)) = initializer_kind { return true; } @@ -3490,7 +3493,7 @@ impl<'c> Translation<'c> { UnaryType(_ty, kind, opt_expr, arg_ty) => { let result = match kind { - UnTypeOp::SizeOf => match opt_expr { + CUnTypeOp::SizeOf => match opt_expr { None => self.compute_size_of_type(ctx, arg_ty.ctype, override_ty)?, Some(_) => { let inner = self.variable_array_base_type(arg_ty.ctype); @@ -3506,10 +3509,10 @@ impl<'c> Translation<'c> { } } }, - UnTypeOp::AlignOf => { + CUnTypeOp::AlignOf => { self.compute_align_of_type(arg_ty.ctype, false, src_loc)? } - UnTypeOp::PreferredAlignOf => { + CUnTypeOp::PreferredAlignOf => { self.compute_align_of_type(arg_ty.ctype, true, src_loc)? } }; diff --git a/c2rust-transpile/src/translator/operators.rs b/c2rust-transpile/src/translator/operators.rs index 17b4892340..cee3cb5f37 100644 --- a/c2rust-transpile/src/translator/operators.rs +++ b/c2rust-transpile/src/translator/operators.rs @@ -1,6 +1,7 @@ //! This module provides translations of unary and binary operator expressions. use super::*; +use crate::c_ast::c_expr::{CBinOp, CUnOp}; fn neg_expr(arg: Box) -> Box { mk().unary_expr(UnOp::Neg(Default::default()), arg) @@ -10,27 +11,27 @@ fn wrapping_neg_expr(arg: Box) -> Box { mk().method_call_expr(arg, "wrapping_neg", vec![]) } -impl From for BinOp { - fn from(op: c_ast::BinOp) -> Self { +impl From for BinOp { + fn from(op: CBinOp) -> Self { match op { - c_ast::BinOp::Multiply => BinOp::Mul(Default::default()), - c_ast::BinOp::Divide => BinOp::Div(Default::default()), - c_ast::BinOp::Modulus => BinOp::Rem(Default::default()), - c_ast::BinOp::Add => BinOp::Add(Default::default()), - c_ast::BinOp::Subtract => BinOp::Sub(Default::default()), - c_ast::BinOp::ShiftLeft => BinOp::Shl(Default::default()), - c_ast::BinOp::ShiftRight => BinOp::Shr(Default::default()), - c_ast::BinOp::Less => BinOp::Lt(Default::default()), - c_ast::BinOp::Greater => BinOp::Gt(Default::default()), - c_ast::BinOp::LessEqual => BinOp::Le(Default::default()), - c_ast::BinOp::GreaterEqual => BinOp::Ge(Default::default()), - c_ast::BinOp::EqualEqual => BinOp::Eq(Default::default()), - c_ast::BinOp::NotEqual => BinOp::Ne(Default::default()), - c_ast::BinOp::BitAnd => BinOp::BitAnd(Default::default()), - c_ast::BinOp::BitXor => BinOp::BitXor(Default::default()), - c_ast::BinOp::BitOr => BinOp::BitOr(Default::default()), - c_ast::BinOp::And => BinOp::And(Default::default()), - c_ast::BinOp::Or => BinOp::Or(Default::default()), + CBinOp::Multiply => BinOp::Mul(Default::default()), + CBinOp::Divide => BinOp::Div(Default::default()), + CBinOp::Modulus => BinOp::Rem(Default::default()), + CBinOp::Add => BinOp::Add(Default::default()), + CBinOp::Subtract => BinOp::Sub(Default::default()), + CBinOp::ShiftLeft => BinOp::Shl(Default::default()), + CBinOp::ShiftRight => BinOp::Shr(Default::default()), + CBinOp::Less => BinOp::Lt(Default::default()), + CBinOp::Greater => BinOp::Gt(Default::default()), + CBinOp::LessEqual => BinOp::Le(Default::default()), + CBinOp::GreaterEqual => BinOp::Ge(Default::default()), + CBinOp::EqualEqual => BinOp::Eq(Default::default()), + CBinOp::NotEqual => BinOp::Ne(Default::default()), + CBinOp::BitAnd => BinOp::BitAnd(Default::default()), + CBinOp::BitXor => BinOp::BitXor(Default::default()), + CBinOp::BitOr => BinOp::BitOr(Default::default()), + CBinOp::And => BinOp::And(Default::default()), + CBinOp::Or => BinOp::Or(Default::default()), _ => panic!("C BinOp {:?} is not a valid Rust BinOp", op), } @@ -42,7 +43,7 @@ impl<'c> Translation<'c> { &self, mut ctx: ExprContext, expr_type_id: CQualTypeId, - op: c_ast::BinOp, + op: CBinOp, lhs: CExprId, rhs: CExprId, opt_lhs_type_id: Option, @@ -56,7 +57,7 @@ impl<'c> Translation<'c> { let lhs_loc = &self.ast_context[lhs].loc; let rhs_loc = &self.ast_context[rhs].loc; - use c_ast::BinOp::*; + use CBinOp::*; match op { Comma => { // The value of the LHS of a comma expression is always discarded @@ -98,7 +99,7 @@ impl<'c> Translation<'c> { // and so we need to decay references to pointers to do so. See // https://github.com/rust-lang/rust/issues/53772. This might be removable // once the above issue is resolved. - if op == c_ast::BinOp::EqualEqual || op == c_ast::BinOp::NotEqual { + if op == CBinOp::EqualEqual || op == CBinOp::NotEqual { ctx = ctx.decay_ref(); } @@ -167,7 +168,7 @@ impl<'c> Translation<'c> { // When we use methods on pointers (ie wrapping_offset_from or offset) // we must ensure we have an explicit raw ptr for the self param, as // self references do not decay - if op == c_ast::BinOp::Subtract || op == c_ast::BinOp::Add { + if op == CBinOp::Subtract || op == CBinOp::Add { let ty_kind = &self.ast_context.resolve_type(lhs_type_id.ctype).kind; if let CTypeKind::Pointer(_) = ty_kind { @@ -202,7 +203,7 @@ impl<'c> Translation<'c> { &self, ctx: ExprContext, bin_op_kind: BinOp, - bin_op: c_ast::BinOp, + bin_op: CBinOp, read: Box, write: Box, rhs: Box, @@ -273,14 +274,14 @@ impl<'c> Translation<'c> { fn convert_assignment_operator( &self, ctx: ExprContext, - op: c_ast::BinOp, + op: CBinOp, expr_type_id: CQualTypeId, lhs: CExprId, rhs: CExprId, compute_lhs_type_id: Option, compute_res_type_id: Option, ) -> TranslationResult>> { - if op == c_ast::BinOp::Assign { + if op == CBinOp::Assign { assert!(compute_lhs_type_id.is_none()); assert!(compute_res_type_id.is_none()); } else { @@ -309,7 +310,7 @@ impl<'c> Translation<'c> { let neither_ptr = !lhs_resolved_ty.kind.is_pointer() && !rhs_resolved_ty.kind.is_pointer(); - use c_ast::BinOp::*; + use CBinOp::*; match op.underlying_assignment() { Some(Add) => neither_ptr, Some(Subtract) => neither_ptr, @@ -352,7 +353,7 @@ impl<'c> Translation<'c> { fn convert_assignment_operator_with_rhs( &self, ctx: ExprContext, - op: c_ast::BinOp, + op: CBinOp, expr_type_id: CQualTypeId, lhs: CExprId, rhs_type_id: CQualTypeId, @@ -360,7 +361,7 @@ impl<'c> Translation<'c> { compute_lhs_type_id: Option, compute_res_type_id: Option, ) -> TranslationResult>> { - if op == c_ast::BinOp::Assign { + if op == CBinOp::Assign { assert!(compute_lhs_type_id.is_none()); assert!(compute_res_type_id.is_none()); } else { @@ -411,11 +412,11 @@ impl<'c> Translation<'c> { }; let is_unsigned_arith = match op { - c_ast::BinOp::AssignAdd - | c_ast::BinOp::AssignSubtract - | c_ast::BinOp::AssignMultiply - | c_ast::BinOp::AssignDivide - | c_ast::BinOp::AssignModulus => compute_resolved_ty.kind.is_unsigned_integral_type(), + CBinOp::AssignAdd + | CBinOp::AssignSubtract + | CBinOp::AssignMultiply + | CBinOp::AssignDivide + | CBinOp::AssignModulus => compute_resolved_ty.kind.is_unsigned_integral_type(), _ => false, }; @@ -440,7 +441,7 @@ impl<'c> Translation<'c> { rvalue: read, }| { // Assignment expression itself - use c_ast::BinOp::*; + use CBinOp::*; let assign_stmt = match op { // Regular (possibly volatile) assignment Assign if !is_volatile => WithStmts::new_val(mk().assign_expr(write, rhs)), @@ -575,7 +576,7 @@ impl<'c> Translation<'c> { fn convert_binary_operator( &self, ctx: ExprContext, - op: c_ast::BinOp, + op: CBinOp, ty: Box, ctype: CTypeId, lhs_type: CQualTypeId, @@ -591,38 +592,36 @@ impl<'c> Translation<'c> { .is_unsigned_integral_type(); Ok(WithStmts::new_val(match op { - c_ast::BinOp::Add => return self.convert_addition(lhs_type, rhs_type, lhs, rhs), - c_ast::BinOp::Subtract => { - return self.convert_subtraction(ty, lhs_type, rhs_type, lhs, rhs) - } + CBinOp::Add => return self.convert_addition(lhs_type, rhs_type, lhs, rhs), + CBinOp::Subtract => return self.convert_subtraction(ty, lhs_type, rhs_type, lhs, rhs), - c_ast::BinOp::Multiply if is_unsigned_integral_type => { + CBinOp::Multiply if is_unsigned_integral_type => { mk().method_call_expr(lhs, "wrapping_mul", vec![rhs]) } - c_ast::BinOp::Multiply => mk().binary_expr(BinOp::Mul(Default::default()), lhs, rhs), + CBinOp::Multiply => mk().binary_expr(BinOp::Mul(Default::default()), lhs, rhs), - c_ast::BinOp::Divide if is_unsigned_integral_type => { + CBinOp::Divide if is_unsigned_integral_type => { mk().method_call_expr(lhs, "wrapping_div", vec![rhs]) } - c_ast::BinOp::Divide => mk().binary_expr(BinOp::Div(Default::default()), lhs, rhs), + CBinOp::Divide => mk().binary_expr(BinOp::Div(Default::default()), lhs, rhs), - c_ast::BinOp::Modulus if is_unsigned_integral_type => { + CBinOp::Modulus if is_unsigned_integral_type => { mk().method_call_expr(lhs, "wrapping_rem", vec![rhs]) } - c_ast::BinOp::Modulus => mk().binary_expr(BinOp::Rem(Default::default()), lhs, rhs), + CBinOp::Modulus => mk().binary_expr(BinOp::Rem(Default::default()), lhs, rhs), - c_ast::BinOp::BitXor => mk().binary_expr(BinOp::BitXor(Default::default()), lhs, rhs), + CBinOp::BitXor => mk().binary_expr(BinOp::BitXor(Default::default()), lhs, rhs), - c_ast::BinOp::ShiftRight => mk().binary_expr(BinOp::Shr(Default::default()), lhs, rhs), - c_ast::BinOp::ShiftLeft => mk().binary_expr(BinOp::Shl(Default::default()), lhs, rhs), + CBinOp::ShiftRight => mk().binary_expr(BinOp::Shr(Default::default()), lhs, rhs), + CBinOp::ShiftLeft => mk().binary_expr(BinOp::Shl(Default::default()), lhs, rhs), - c_ast::BinOp::EqualEqual | c_ast::BinOp::NotEqual => { + CBinOp::EqualEqual | CBinOp::NotEqual => { // Using `.is_none()` and `.is_some()` for null comparison means // we don't have to rely on `trait PartialEq` as much // and it is also more idiomatic. let (is_null, bin_op) = match op { - c_ast::BinOp::EqualEqual => (true, BinOp::Eq(Default::default())), - c_ast::BinOp::NotEqual => (false, BinOp::Ne(Default::default())), + CBinOp::EqualEqual => (true, BinOp::Eq(Default::default())), + CBinOp::NotEqual => (false, BinOp::Ne(Default::default())), _ => unreachable!(), }; let expr = match lhs_rhs_ids { @@ -637,21 +636,19 @@ impl<'c> Translation<'c> { bool_to_int(expr) } - c_ast::BinOp::Less => { - bool_to_int(mk().binary_expr(BinOp::Lt(Default::default()), lhs, rhs)) - } - c_ast::BinOp::Greater => { + CBinOp::Less => bool_to_int(mk().binary_expr(BinOp::Lt(Default::default()), lhs, rhs)), + CBinOp::Greater => { bool_to_int(mk().binary_expr(BinOp::Gt(Default::default()), lhs, rhs)) } - c_ast::BinOp::GreaterEqual => { + CBinOp::GreaterEqual => { bool_to_int(mk().binary_expr(BinOp::Ge(Default::default()), lhs, rhs)) } - c_ast::BinOp::LessEqual => { + CBinOp::LessEqual => { bool_to_int(mk().binary_expr(BinOp::Le(Default::default()), lhs, rhs)) } - c_ast::BinOp::BitAnd => mk().binary_expr(BinOp::BitAnd(Default::default()), lhs, rhs), - c_ast::BinOp::BitOr => mk().binary_expr(BinOp::BitOr(Default::default()), lhs, rhs), + CBinOp::BitAnd => mk().binary_expr(BinOp::BitAnd(Default::default()), lhs, rhs), + CBinOp::BitOr => mk().binary_expr(BinOp::BitOr(Default::default()), lhs, rhs), op => unimplemented!("Translation of binary operator {:?}", op), })) @@ -731,9 +728,9 @@ impl<'c> Translation<'c> { arg: CExprId, ) -> TranslationResult>> { let op = if up { - c_ast::BinOp::AssignAdd + CBinOp::AssignAdd } else { - c_ast::BinOp::AssignSubtract + CBinOp::AssignSubtract }; let one = match self.ast_context.resolve_type(ty.ctype).kind { // TODO: If rust gets f16 support: @@ -870,7 +867,7 @@ impl<'c> Translation<'c> { pub fn convert_unary_operator( &self, ctx: ExprContext, - name: c_ast::UnOp, + name: CUnOp, cqual_type: CQualTypeId, arg: CExprId, lrvalue: LRValue, @@ -879,15 +876,15 @@ impl<'c> Translation<'c> { let resolved_ctype = self.ast_context.resolve_type(ctype); let mut unary = match name { - c_ast::UnOp::AddressOf => self.convert_address_of(ctx, cqual_type, arg), - c_ast::UnOp::PreIncrement => self.convert_pre_increment(ctx, cqual_type, true, arg), - c_ast::UnOp::PreDecrement => self.convert_pre_increment(ctx, cqual_type, false, arg), - c_ast::UnOp::PostIncrement => self.convert_post_increment(ctx, cqual_type, true, arg), - c_ast::UnOp::PostDecrement => self.convert_post_increment(ctx, cqual_type, false, arg), - c_ast::UnOp::Deref => self.convert_deref(ctx, cqual_type, arg, lrvalue), - c_ast::UnOp::Plus => self.convert_expr(ctx.used(), arg, Some(cqual_type)), // promotion is explicit in the clang AST - - c_ast::UnOp::Negate => { + CUnOp::AddressOf => self.convert_address_of(ctx, cqual_type, arg), + CUnOp::PreIncrement => self.convert_pre_increment(ctx, cqual_type, true, arg), + CUnOp::PreDecrement => self.convert_pre_increment(ctx, cqual_type, false, arg), + CUnOp::PostIncrement => self.convert_post_increment(ctx, cqual_type, true, arg), + CUnOp::PostDecrement => self.convert_post_increment(ctx, cqual_type, false, arg), + CUnOp::Deref => self.convert_deref(ctx, cqual_type, arg, lrvalue), + CUnOp::Plus => self.convert_expr(ctx.used(), arg, Some(cqual_type)), // promotion is explicit in the clang AST + + CUnOp::Negate => { let val = self.convert_expr(ctx.used(), arg, Some(cqual_type))?; if resolved_ctype.kind.is_unsigned_integral_type() { @@ -896,19 +893,19 @@ impl<'c> Translation<'c> { Ok(val.map(neg_expr)) } } - c_ast::UnOp::Complement => Ok(self + CUnOp::Complement => Ok(self .convert_expr(ctx.used(), arg, Some(cqual_type))? .map(|a| mk().unary_expr(UnOp::Not(Default::default()), a))), - c_ast::UnOp::Not => { + CUnOp::Not => { let val = self.convert_condition(ctx, false, arg)?; Ok(val.map(|x| mk().cast_expr(x, mk().abs_path_ty(vec!["core", "ffi", "c_int"])))) } - c_ast::UnOp::Extension => { + CUnOp::Extension => { let arg = self.convert_expr(ctx, arg, Some(cqual_type))?; Ok(arg) } - c_ast::UnOp::Real | c_ast::UnOp::Imag | c_ast::UnOp::Coawait => { + CUnOp::Real | CUnOp::Imag | CUnOp::Coawait => { panic!("Unsupported extension operator") } }?; @@ -920,11 +917,11 @@ impl<'c> Translation<'c> { // it's a no-op around the inner expression. if !matches!( name, - c_ast::UnOp::PreDecrement - | c_ast::UnOp::PreIncrement - | c_ast::UnOp::PostDecrement - | c_ast::UnOp::PostIncrement - | c_ast::UnOp::Extension + CUnOp::PreDecrement + | CUnOp::PreIncrement + | CUnOp::PostDecrement + | CUnOp::PostIncrement + | CUnOp::Extension ) { unary = self.convert_side_effects_expr( ctx, diff --git a/c2rust-transpile/src/translator/pointers.rs b/c2rust-transpile/src/translator/pointers.rs index d7f42fb347..b31d90ffc1 100644 --- a/c2rust-transpile/src/translator/pointers.rs +++ b/c2rust-transpile/src/translator/pointers.rs @@ -5,14 +5,14 @@ use c2rust_ast_exporter::clang_ast::LRValue; use failure::{err_msg, format_err}; use syn::{BinOp, Expr, Type, UnOp}; -use crate::{ - c_ast, - diagnostics::{TranslationError, TranslationErrorKind, TranslationResult}, - format_translation_err, - translator::{cast_int, transmute_expr, unwrap_function_pointer, ExprContext, Translation}, - with_stmts::WithStmts, - CExprId, CExprKind, CLiteral, CQualTypeId, CTypeId, CTypeKind, CastKind, ExternCrate, +use crate::c_ast::c_expr::{CExprId, CExprKind, CLiteral, CUnOp, CastKind}; +use crate::c_ast::c_type::{CQualTypeId, CTypeId, CTypeKind}; +use crate::diagnostics::{TranslationError, TranslationErrorKind, TranslationResult}; +use crate::translator::{ + cast_int, format_translation_err, transmute_expr, unwrap_function_pointer, ExprContext, + ExternCrate, Translation, }; +use crate::with_stmts::WithStmts; impl<'c> Translation<'c> { pub fn convert_address_of( @@ -25,7 +25,7 @@ impl<'c> Translation<'c> { match arg_kind { // C99 6.5.3.2 para 4 - CExprKind::Unary(_, c_ast::UnOp::Deref, target, _) => { + CExprKind::Unary(_, CUnOp::Deref, target, _) => { return self.convert_expr(ctx, *target, None) } // Array subscript functions as a deref too. @@ -210,7 +210,7 @@ impl<'c> Translation<'c> { ) -> TranslationResult>> { let arg_expr_kind = &self.ast_context.index(arg).kind; - if let &CExprKind::Unary(_, c_ast::UnOp::AddressOf, arg, _) = arg_expr_kind { + if let &CExprKind::Unary(_, CUnOp::AddressOf, arg, _) = arg_expr_kind { return self.convert_expr(ctx.used(), arg, None); } diff --git a/c2rust-transpile/src/translator/simd.rs b/c2rust-transpile/src/translator/simd.rs index a9c8a1947f..747ff60214 100644 --- a/c2rust-transpile/src/translator/simd.rs +++ b/c2rust-transpile/src/translator/simd.rs @@ -3,11 +3,13 @@ use super::*; -use crate::c_ast::BinOp::{Add, BitAnd, ShiftRight}; -use crate::c_ast::CExprKind::{Binary, Call, Conditional, ExplicitCast, ImplicitCast, Literal}; -use crate::c_ast::CLiteral::Integer; -use crate::c_ast::CTypeKind::{Char, Double, Float, Int, LongLong, Short}; -use crate::c_ast::CastKind::{BitCast, IntegralCast}; +use crate::c_ast::c_expr::CBinOp::{Add, BitAnd, ShiftRight}; +use crate::c_ast::c_expr::CExprKind::{ + Binary, Call, Conditional, ExplicitCast, ImplicitCast, Literal, +}; +use crate::c_ast::c_expr::CLiteral::Integer; +use crate::c_ast::c_expr::CastKind::{BitCast, IntegralCast}; +use crate::c_ast::c_type::CTypeKind::{Char, Double, Float, Int, LongLong, Short}; /// As of rustc 1.29, rust is known to be missing some SIMD functions. /// See diff --git a/c2rust-transpile/src/translator/structs_unions.rs b/c2rust-transpile/src/translator/structs_unions.rs index b754159e9b..0e9569ce04 100644 --- a/c2rust-transpile/src/translator/structs_unions.rs +++ b/c2rust-transpile/src/translator/structs_unions.rs @@ -6,11 +6,9 @@ use std::ops::Index; use super::named_references::NamedReference; use super::TranslationError; -use crate::c_ast; -use crate::c_ast::{ - BinOp, CDeclId, CDeclKind, CExprId, CExprKind, CFieldId, CQualTypeId, CRecordId, CTypeId, - MemberKind, -}; +use crate::c_ast::c_decl::{CDeclId, CDeclKind, CFieldId, CRecordId}; +use crate::c_ast::c_expr::{CBinOp, CExprId, CExprKind, CUnOp, MemberKind}; +use crate::c_ast::c_type::{CQualTypeId, CTypeId}; use crate::diagnostics::TranslationResult; use crate::translator::variadic::mk_va_list_ty; use crate::translator::{ConvertedDecl, ExprContext, Translation, PADDING_SUFFIX}; @@ -20,8 +18,8 @@ use c2rust_ast_builder::mk; use c2rust_ast_printer::pprust; use proc_macro2::Span; use syn::{ - self, BinOp as RBinOp, Expr, ExprAssign, ExprBinary, ExprBlock, ExprCast, ExprMethodCall, - ExprUnary, Field, Stmt, Type, UnOp, + self, BinOp, Expr, ExprAssign, ExprBinary, ExprBlock, ExprCast, ExprMethodCall, ExprUnary, + Field, Stmt, Type, UnOp, }; use itertools::EitherOrBoth::{Both, Right}; @@ -151,7 +149,7 @@ impl<'a> Translation<'a> { let outer_size = self.mk_size_of_ty_expr(outer_ty)?.to_expr(); let inner_size = self.mk_size_of_ty_expr(inner_ty)?.to_expr(); let padding_value = - mk().binary_expr(RBinOp::Sub(Default::default()), outer_size, inner_size); + mk().binary_expr(BinOp::Sub(Default::default()), outer_size, inner_size); let padding_const = mk() .span(span) .call_attr("allow", vec!["dead_code", "non_upper_case_globals"]) @@ -712,7 +710,7 @@ impl<'a> Translation<'a> { pub fn convert_bitfield_assignment_op_with_rhs( &self, ctx: ExprContext, - op: BinOp, + op: CBinOp, lhs: CExprId, rhs_expr: Box, field_id: CDeclId, @@ -733,41 +731,37 @@ impl<'a> Translation<'a> { // Allow the value of this assignment to be used as the RHS of other assignments let val = lhs_expr_read.clone(); let param_expr = match op { - BinOp::AssignAdd => { - mk().binary_expr(RBinOp::Add(Default::default()), lhs_expr_read, rhs_expr) + CBinOp::AssignAdd => { + mk().binary_expr(BinOp::Add(Default::default()), lhs_expr_read, rhs_expr) + } + CBinOp::AssignSubtract => { + mk().binary_expr(BinOp::Sub(Default::default()), lhs_expr_read, rhs_expr) } - BinOp::AssignSubtract => { - mk().binary_expr(RBinOp::Sub(Default::default()), lhs_expr_read, rhs_expr) + CBinOp::AssignMultiply => { + mk().binary_expr(BinOp::Mul(Default::default()), lhs_expr_read, rhs_expr) } - BinOp::AssignMultiply => { - mk().binary_expr(RBinOp::Mul(Default::default()), lhs_expr_read, rhs_expr) + CBinOp::AssignDivide => { + mk().binary_expr(BinOp::Div(Default::default()), lhs_expr_read, rhs_expr) } - BinOp::AssignDivide => { - mk().binary_expr(RBinOp::Div(Default::default()), lhs_expr_read, rhs_expr) + CBinOp::AssignModulus => { + mk().binary_expr(BinOp::Rem(Default::default()), lhs_expr_read, rhs_expr) } - BinOp::AssignModulus => { - mk().binary_expr(RBinOp::Rem(Default::default()), lhs_expr_read, rhs_expr) + CBinOp::AssignBitXor => { + mk().binary_expr(BinOp::BitXor(Default::default()), lhs_expr_read, rhs_expr) } - BinOp::AssignBitXor => mk().binary_expr( - RBinOp::BitXor(Default::default()), - lhs_expr_read, - rhs_expr, - ), - BinOp::AssignShiftLeft => { - mk().binary_expr(RBinOp::Shl(Default::default()), lhs_expr_read, rhs_expr) + CBinOp::AssignShiftLeft => { + mk().binary_expr(BinOp::Shl(Default::default()), lhs_expr_read, rhs_expr) } - BinOp::AssignShiftRight => { - mk().binary_expr(RBinOp::Shr(Default::default()), lhs_expr_read, rhs_expr) + CBinOp::AssignShiftRight => { + mk().binary_expr(BinOp::Shr(Default::default()), lhs_expr_read, rhs_expr) } - BinOp::AssignBitOr => { - mk().binary_expr(RBinOp::BitOr(Default::default()), lhs_expr_read, rhs_expr) + CBinOp::AssignBitOr => { + mk().binary_expr(BinOp::BitOr(Default::default()), lhs_expr_read, rhs_expr) } - BinOp::AssignBitAnd => mk().binary_expr( - RBinOp::BitAnd(Default::default()), - lhs_expr_read, - rhs_expr, - ), - BinOp::Assign => rhs_expr, + CBinOp::AssignBitAnd => { + mk().binary_expr(BinOp::BitAnd(Default::default()), lhs_expr_read, rhs_expr) + } + CBinOp::Assign => rhs_expr, _ => panic!("Cannot convert non-assignment operator"), }; @@ -1033,7 +1027,7 @@ impl<'a> Translation<'a> { let mut val = match kind { MemberKind::Dot => self.convert_expr(ctx, expr, None)?, MemberKind::Arrow => { - if let CExprKind::Unary(_, c_ast::UnOp::AddressOf, subexpr_id, _) = + if let CExprKind::Unary(_, CUnOp::AddressOf, subexpr_id, _) = self.ast_context[expr].kind { // Special-case the `(&x)->field` pattern @@ -1046,7 +1040,11 @@ impl<'a> Translation<'a> { } }; - let record_id = self.ast_context.parents[&decl]; + let record_id = self + .ast_context + .parent_with_type(decl) + .expect("Field does not have a parent Struct or Union"); + if self.ast_context.has_inner_struct_decl(record_id) { // The structure is split into an outer and an inner, // so we need to go through the outer structure to the inner one @@ -1099,7 +1097,10 @@ impl<'a> Translation<'a> { opt_field_id: Option, ) -> TranslationResult>> { let field_id = opt_field_id.expect("Missing field ID in union cast"); - let union_id = self.ast_context.parents[&field_id]; + let union_id = self + .ast_context + .parent_with_type(field_id) + .expect("Union field does not have a parent Union"); let union_name = self .type_converter