From 0c92b417eb4ed219ac625062300c762ca35e33b2 Mon Sep 17 00:00:00 2001 From: Rua Date: Tue, 10 Mar 2026 14:54:11 +0100 Subject: [PATCH 1/8] transpile: Expand all `c_ast::*` globs --- c2rust-transpile/src/c_ast/conversion.rs | 12 ++++++++++-- c2rust-transpile/src/c_ast/iterators.rs | 5 ++++- c2rust-transpile/src/c_ast/mod.rs | 3 +-- c2rust-transpile/src/c_ast/print.rs | 5 ++++- c2rust-transpile/src/cfg/mod.rs | 5 ++++- c2rust-transpile/src/convert_type.rs | 4 +++- c2rust-transpile/src/lib.rs | 3 +-- c2rust-transpile/src/translator/enums.rs | 10 +++++----- c2rust-transpile/src/translator/mod.rs | 8 +++++++- c2rust-transpile/src/translator/pointers.rs | 14 +++++++------- 10 files changed, 46 insertions(+), 23 deletions(-) diff --git a/c2rust-transpile/src/c_ast/conversion.rs b/c2rust-transpile/src/c_ast/conversion.rs index 59cc7bcfa8..d86e1c8e5d 100644 --- a/c2rust-transpile/src/c_ast/conversion.rs +++ b/c2rust-transpile/src/c_ast/conversion.rs @@ -1,9 +1,17 @@ -use crate::c_ast::*; +use crate::c_ast::{ + AsmOperand, Attribute, BinOp, CDecl, CDeclId, CDeclKind, CExpr, CExprId, CExprKind, CLiteral, + CQualTypeId, CStmt, CStmtId, CStmtKind, CType, CTypeId, CTypeKind, CastKind, ConstIntExpr, + Designator, DisplaySrcSpan, IntBase, MemberKind, OffsetOfKind, Qualifiers, TypedAstContext, + UnOp, UnTypeOp, +}; 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; diff --git a/c2rust-transpile/src/c_ast/iterators.rs b/c2rust-transpile/src/c_ast/iterators.rs index 38bcffe6a8..7a53d0a7ab 100644 --- a/c2rust-transpile/src/c_ast/iterators.rs +++ b/c2rust-transpile/src/c_ast/iterators.rs @@ -1,4 +1,7 @@ -use crate::c_ast::*; +use crate::c_ast::{ + CDeclId, CDeclKind, CExprId, CExprKind, CStmtId, CStmtKind, CTypeId, CTypeKind, OffsetOfKind, + TypedAstContext, +}; #[derive(Copy, Clone, Eq, PartialEq, Debug, Hash)] pub enum SomeId { diff --git a/c2rust-transpile/src/c_ast/mod.rs b/c2rust-transpile/src/c_ast/mod.rs index c01937bf6c..48c61aac10 100644 --- a/c2rust-transpile/src/c_ast/mod.rs +++ b/c2rust-transpile/src/c_ast/mod.rs @@ -1,5 +1,4 @@ -use crate::c_ast::iterators::{immediate_children_all_types, NodeVisitor}; -use crate::iterators::{DFNodes, SomeId}; +use crate::c_ast::iterators::{immediate_children_all_types, DFNodes, NodeVisitor, SomeId}; use c2rust_ast_exporter::clang_ast::LRValue; use indexmap::{IndexMap, IndexSet}; use itertools::Itertools; diff --git a/c2rust-transpile/src/c_ast/print.rs b/c2rust-transpile/src/c_ast/print.rs index 1c2c23d2df..31cdc73e22 100644 --- a/c2rust-transpile/src/c_ast/print.rs +++ b/c2rust-transpile/src/c_ast/print.rs @@ -1,4 +1,7 @@ -use crate::c_ast::*; +use crate::c_ast::{ + BinOp, CDeclId, CDeclKind, CExprId, CExprKind, CLiteral, CQualTypeId, CStmtId, CStmtKind, + CTypeId, CTypeKind, MemberKind, OffsetOfKind, Qualifiers, TypedAstContext, UnOp, +}; use std::io::{Result, Write}; pub struct Printer { diff --git a/c2rust-transpile/src/cfg/mod.rs b/c2rust-transpile/src/cfg/mod.rs index 76549ee735..a28b62c552 100644 --- a/c2rust-transpile/src/cfg/mod.rs +++ b/c2rust-transpile/src/cfg/mod.rs @@ -42,7 +42,10 @@ use serde::ser::{ }; use serde_json; -use crate::c_ast::*; +use crate::c_ast::{ + CDeclId, CExprId, CExprKind, CQualTypeId, CStmtId, CStmtKind, ConstIntExpr, TypedAstContext, + UnOp, +}; use crate::translator::*; use crate::with_stmts::WithStmts; use c2rust_ast_builder::mk; diff --git a/c2rust-transpile/src/convert_type.rs b/c2rust-transpile/src/convert_type.rs index 6cf037d587..01fac08fa2 100644 --- a/c2rust-transpile/src/convert_type.rs +++ b/c2rust-transpile/src/convert_type.rs @@ -1,5 +1,7 @@ use crate::c_ast::CDeclId; -use crate::c_ast::*; +use crate::c_ast::{ + CDeclKind, CFieldId, CParamId, CQualTypeId, CRecordId, CTypeId, CTypeKind, 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/enums.rs b/c2rust-transpile/src/translator/enums.rs index afaa10164c..dcedf940fa 100644 --- a/c2rust-transpile/src/translator/enums.rs +++ b/c2rust-transpile/src/translator/enums.rs @@ -2,14 +2,14 @@ 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, +use crate::c_ast; +use crate::c_ast::{ CDeclKind, CEnumConstantId, CEnumId, CExprId, CExprKind, CLiteral, CQualTypeId, CTypeId, CTypeKind, ConstIntExpr, }; +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( diff --git a/c2rust-transpile/src/translator/mod.rs b/c2rust-transpile/src/translator/mod.rs index 13190694a3..3daf9e0b9b 100644 --- a/c2rust-transpile/src/translator/mod.rs +++ b/c2rust-transpile/src/translator/mod.rs @@ -34,15 +34,21 @@ 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::{ + AsmOperand, CDecl, CDeclId, CDeclKind, CDeclSrcRange, CExprId, CExprKind, CFieldId, CLiteral, + CQualTypeId, CStmtId, CStmtKind, CTypeId, CTypeKind, CastKind, CommentContext, ConstIntExpr, + FileId, IntBase, Located, OffsetOfKind, SrcLoc, TypedAstContext, UnTypeOp, +}; 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; diff --git a/c2rust-transpile/src/translator/pointers.rs b/c2rust-transpile/src/translator/pointers.rs index d7f42fb347..c16ac147c6 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; +use crate::c_ast::{CExprId, CExprKind, CLiteral, CQualTypeId, CTypeId, CTypeKind, CastKind}; +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( From 598f1a57df8bd0d7ae7985d085170c18da701a6b Mon Sep 17 00:00:00 2001 From: Rua Date: Tue, 10 Mar 2026 15:01:14 +0100 Subject: [PATCH 2/8] transpile: Split off c_ast/c_type.rs module --- c2rust-transpile/src/c_ast/c_type.rs | 826 ++++++++++++++++++ c2rust-transpile/src/c_ast/conversion.rs | 6 +- c2rust-transpile/src/c_ast/iterators.rs | 6 +- c2rust-transpile/src/c_ast/mod.rs | 817 +---------------- c2rust-transpile/src/c_ast/print.rs | 5 +- c2rust-transpile/src/cfg/mod.rs | 4 +- c2rust-transpile/src/convert_type.rs | 5 +- c2rust-transpile/src/translator/enums.rs | 4 +- c2rust-transpile/src/translator/mod.rs | 7 +- c2rust-transpile/src/translator/pointers.rs | 3 +- c2rust-transpile/src/translator/simd.rs | 2 +- .../src/translator/structs_unions.rs | 4 +- 12 files changed, 852 insertions(+), 837 deletions(-) create mode 100644 c2rust-transpile/src/c_ast/c_type.rs 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..ebbf6e0af7 --- /dev/null +++ b/c2rust-transpile/src/c_ast/c_type.rs @@ -0,0 +1,826 @@ +use crate::c_ast::{ + Attribute, CDeclId, CDeclKind, CEnumId, CExprId, CRecordId, CTypedefId, 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 d86e1c8e5d..9173d6151f 100644 --- a/c2rust-transpile/src/c_ast/conversion.rs +++ b/c2rust-transpile/src/c_ast/conversion.rs @@ -1,8 +1,8 @@ +use crate::c_ast::c_type::{CQualTypeId, CType, CTypeId, CTypeKind, Qualifiers}; use crate::c_ast::{ AsmOperand, Attribute, BinOp, CDecl, CDeclId, CDeclKind, CExpr, CExprId, CExprKind, CLiteral, - CQualTypeId, CStmt, CStmtId, CStmtKind, CType, CTypeId, CTypeKind, CastKind, ConstIntExpr, - Designator, DisplaySrcSpan, IntBase, MemberKind, OffsetOfKind, Qualifiers, TypedAstContext, - UnOp, UnTypeOp, + CStmt, CStmtId, CStmtKind, CastKind, ConstIntExpr, Designator, DisplaySrcSpan, IntBase, + MemberKind, OffsetOfKind, TypedAstContext, UnOp, UnTypeOp, }; use crate::diagnostics::diag; use c2rust_ast_exporter::clang_ast::*; diff --git a/c2rust-transpile/src/c_ast/iterators.rs b/c2rust-transpile/src/c_ast/iterators.rs index 7a53d0a7ab..2ec3d7d5a0 100644 --- a/c2rust-transpile/src/c_ast/iterators.rs +++ b/c2rust-transpile/src/c_ast/iterators.rs @@ -1,6 +1,6 @@ +use crate::c_ast::c_type::{CTypeId, CTypeKind}; use crate::c_ast::{ - CDeclId, CDeclKind, CExprId, CExprKind, CStmtId, CStmtKind, CTypeId, CTypeKind, OffsetOfKind, - TypedAstContext, + CDeclId, CDeclKind, CExprId, CExprKind, CStmtId, CStmtKind, OffsetOfKind, TypedAstContext, }; #[derive(Copy, Clone, Eq, PartialEq, Debug, Hash)] @@ -273,7 +273,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 48c61aac10..fdc63bc536 100644 --- a/c2rust-transpile/src/c_ast/mod.rs +++ b/c2rust-transpile/src/c_ast/mod.rs @@ -1,3 +1,4 @@ +use crate::c_ast::c_type::{CFuncTypeId, CQualTypeId, CType, CTypeId, CTypeKind}; use crate::c_ast::iterators::{immediate_children_all_types, DFNodes, NodeVisitor, SomeId}; use c2rust_ast_exporter::clang_ast::LRValue; use indexmap::{IndexMap, IndexSet}; @@ -15,13 +16,11 @@ pub use self::conversion::*; pub use self::print::Printer; pub use c2rust_ast_exporter::clang_ast::{BuiltinVaListKind, SrcFile, SrcLoc, SrcSpan}; +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); @@ -35,7 +34,6 @@ pub struct CStmtId(pub u64); 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' @@ -462,178 +460,6 @@ 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 - } - } - /// 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 { @@ -714,34 +540,6 @@ impl TypedAstContext { 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> { @@ -1256,19 +1054,6 @@ impl TypedAstContext { } ) } - - 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 { @@ -1381,17 +1166,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 { @@ -1439,7 +1213,6 @@ impl Index for TypedAstContext { pub type CDecl = Located; pub type CStmt = Located; pub type CExpr = Located; -pub type CType = Located; #[derive(Debug, Clone)] pub enum CDeclKind { @@ -2154,432 +1927,6 @@ pub struct AsmOperand { 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), @@ -2613,166 +1960,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; diff --git a/c2rust-transpile/src/c_ast/print.rs b/c2rust-transpile/src/c_ast/print.rs index 31cdc73e22..e9253d3461 100644 --- a/c2rust-transpile/src/c_ast/print.rs +++ b/c2rust-transpile/src/c_ast/print.rs @@ -1,6 +1,7 @@ +use crate::c_ast::c_type::{CQualTypeId, CTypeId, CTypeKind, Qualifiers}; use crate::c_ast::{ - BinOp, CDeclId, CDeclKind, CExprId, CExprKind, CLiteral, CQualTypeId, CStmtId, CStmtKind, - CTypeId, CTypeKind, MemberKind, OffsetOfKind, Qualifiers, TypedAstContext, UnOp, + BinOp, CDeclId, CDeclKind, CExprId, CExprKind, CLiteral, CStmtId, CStmtKind, MemberKind, + OffsetOfKind, TypedAstContext, UnOp, }; use std::io::{Result, Write}; diff --git a/c2rust-transpile/src/cfg/mod.rs b/c2rust-transpile/src/cfg/mod.rs index a28b62c552..1b344bd7a8 100644 --- a/c2rust-transpile/src/cfg/mod.rs +++ b/c2rust-transpile/src/cfg/mod.rs @@ -42,9 +42,9 @@ use serde::ser::{ }; use serde_json; +use crate::c_ast::c_type::CQualTypeId; use crate::c_ast::{ - CDeclId, CExprId, CExprKind, CQualTypeId, CStmtId, CStmtKind, ConstIntExpr, TypedAstContext, - UnOp, + CDeclId, CExprId, CExprKind, CStmtId, CStmtKind, ConstIntExpr, TypedAstContext, UnOp, }; use crate::translator::*; use crate::with_stmts::WithStmts; diff --git a/c2rust-transpile/src/convert_type.rs b/c2rust-transpile/src/convert_type.rs index 01fac08fa2..89c880c3ab 100644 --- a/c2rust-transpile/src/convert_type.rs +++ b/c2rust-transpile/src/convert_type.rs @@ -1,7 +1,6 @@ +use crate::c_ast::c_type::{CQualTypeId, CTypeId, CTypeKind}; use crate::c_ast::CDeclId; -use crate::c_ast::{ - CDeclKind, CFieldId, CParamId, CQualTypeId, CRecordId, CTypeId, CTypeKind, TypedAstContext, -}; +use crate::c_ast::{CDeclKind, CFieldId, CParamId, CRecordId, TypedAstContext}; use crate::diagnostics::TranslationResult; use crate::renamer::*; use crate::translator::variadic::mk_va_list_ty; diff --git a/c2rust-transpile/src/translator/enums.rs b/c2rust-transpile/src/translator/enums.rs index dcedf940fa..e88a7a65b6 100644 --- a/c2rust-transpile/src/translator/enums.rs +++ b/c2rust-transpile/src/translator/enums.rs @@ -3,9 +3,9 @@ use proc_macro2::Span; use syn::Expr; use crate::c_ast; +use crate::c_ast::c_type::{CQualTypeId, CTypeId, CTypeKind}; use crate::c_ast::{ - CDeclKind, CEnumConstantId, CEnumId, CExprId, CExprKind, CLiteral, CQualTypeId, CTypeId, - CTypeKind, ConstIntExpr, + CDeclKind, CEnumConstantId, CEnumId, CExprId, CExprKind, CLiteral, ConstIntExpr, }; use crate::diagnostics::TranslationResult; use crate::translator::{signed_int_expr, ConvertedDecl, ExprContext, Translation}; diff --git a/c2rust-transpile/src/translator/mod.rs b/c2rust-transpile/src/translator/mod.rs index 3daf9e0b9b..e01496f453 100644 --- a/c2rust-transpile/src/translator/mod.rs +++ b/c2rust-transpile/src/translator/mod.rs @@ -37,11 +37,12 @@ use c2rust_ast_builder::{mk, properties::*, Builder}; use c2rust_ast_exporter::clang_ast::SrcSpan; use c2rust_ast_printer::pprust; +use crate::c_ast::c_type::{CQualTypeId, CTypeId, CTypeKind}; use crate::c_ast::iterators::{DFExpr, SomeId}; use crate::c_ast::{ AsmOperand, CDecl, CDeclId, CDeclKind, CDeclSrcRange, CExprId, CExprKind, CFieldId, CLiteral, - CQualTypeId, CStmtId, CStmtKind, CTypeId, CTypeKind, CastKind, CommentContext, ConstIntExpr, - FileId, IntBase, Located, OffsetOfKind, SrcLoc, TypedAstContext, UnTypeOp, + CStmtId, CStmtKind, CastKind, CommentContext, ConstIntExpr, FileId, IntBase, Located, + OffsetOfKind, TypedAstContext, UnTypeOp, }; use crate::cfg; use crate::convert_type::TypeConverter; @@ -50,7 +51,7 @@ use crate::with_stmts::WithStmts; use crate::TranslateMacros; use crate::{c_ast, format_translation_err}; use crate::{ExternCrate, TranspilerConfig}; -use c2rust_ast_exporter::clang_ast::LRValue; +use c2rust_ast_exporter::clang_ast::{LRValue, SrcLoc}; mod assembly; mod atomics; diff --git a/c2rust-transpile/src/translator/pointers.rs b/c2rust-transpile/src/translator/pointers.rs index c16ac147c6..f64111e378 100644 --- a/c2rust-transpile/src/translator/pointers.rs +++ b/c2rust-transpile/src/translator/pointers.rs @@ -6,7 +6,8 @@ use failure::{err_msg, format_err}; use syn::{BinOp, Expr, Type, UnOp}; use crate::c_ast; -use crate::c_ast::{CExprId, CExprKind, CLiteral, CQualTypeId, CTypeId, CTypeKind, CastKind}; +use crate::c_ast::c_type::{CQualTypeId, CTypeId, CTypeKind}; +use crate::c_ast::{CExprId, CExprKind, CLiteral, CastKind}; use crate::diagnostics::{TranslationError, TranslationErrorKind, TranslationResult}; use crate::translator::{ cast_int, format_translation_err, transmute_expr, unwrap_function_pointer, ExprContext, diff --git a/c2rust-transpile/src/translator/simd.rs b/c2rust-transpile/src/translator/simd.rs index a9c8a1947f..81bb080d1d 100644 --- a/c2rust-transpile/src/translator/simd.rs +++ b/c2rust-transpile/src/translator/simd.rs @@ -3,10 +3,10 @@ use super::*; +use crate::c_ast::c_type::CTypeKind::{Char, Double, Float, Int, LongLong, Short}; 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}; /// As of rustc 1.29, rust is known to be missing some SIMD functions. diff --git a/c2rust-transpile/src/translator/structs_unions.rs b/c2rust-transpile/src/translator/structs_unions.rs index b754159e9b..1c00369548 100644 --- a/c2rust-transpile/src/translator/structs_unions.rs +++ b/c2rust-transpile/src/translator/structs_unions.rs @@ -7,9 +7,9 @@ use std::ops::Index; use super::named_references::NamedReference; use super::TranslationError; use crate::c_ast; +use crate::c_ast::c_type::{CQualTypeId, CTypeId}; use crate::c_ast::{ - BinOp, CDeclId, CDeclKind, CExprId, CExprKind, CFieldId, CQualTypeId, CRecordId, CTypeId, - MemberKind, + BinOp, CDeclId, CDeclKind, CExprId, CExprKind, CFieldId, CRecordId, MemberKind, }; use crate::diagnostics::TranslationResult; use crate::translator::variadic::mk_va_list_ty; From 6efc732ece5217b90c3d06c79a0afd257a3cc3b0 Mon Sep 17 00:00:00 2001 From: Rua Date: Tue, 10 Mar 2026 15:18:25 +0100 Subject: [PATCH 3/8] transpile: Split off c_ast/c_expr.rs module --- c2rust-transpile/src/c_ast/c_expr.rs | 817 ++++++++++++++++++ c2rust-transpile/src/c_ast/c_type.rs | 4 +- c2rust-transpile/src/c_ast/conversion.rs | 9 +- c2rust-transpile/src/c_ast/iterators.rs | 9 +- c2rust-transpile/src/c_ast/mod.rs | 812 +---------------- c2rust-transpile/src/c_ast/print.rs | 6 +- c2rust-transpile/src/cfg/mod.rs | 5 +- c2rust-transpile/src/translator/enums.rs | 7 +- c2rust-transpile/src/translator/mod.rs | 26 +- c2rust-transpile/src/translator/operators.rs | 186 ++-- c2rust-transpile/src/translator/pointers.rs | 6 +- c2rust-transpile/src/translator/simd.rs | 10 +- .../src/translator/structs_unions.rs | 7 +- 13 files changed, 972 insertions(+), 932 deletions(-) create mode 100644 c2rust-transpile/src/c_ast/c_expr.rs 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..1fdf66b40d --- /dev/null +++ b/c2rust-transpile/src/c_ast/c_expr.rs @@ -0,0 +1,817 @@ +use crate::c_ast::c_type::{CQualTypeId, CTypeId, CTypeKind}; +use crate::c_ast::{CDeclId, CDeclKind, CFieldId, CStmtId, Located, 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, 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, +} + +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 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), +} + +#[derive(Copy, Clone, Debug)] +pub enum Designator { + Index(u64), + Range(u64, u64), + Field(CFieldId), +} + +impl TypedAstContext { + 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(_, 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)) + } + + /// 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(_, 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, + } + } +} + +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_type.rs b/c2rust-transpile/src/c_ast/c_type.rs index ebbf6e0af7..c71231c89c 100644 --- a/c2rust-transpile/src/c_ast/c_type.rs +++ b/c2rust-transpile/src/c_ast/c_type.rs @@ -1,6 +1,6 @@ +use crate::c_ast::c_expr::CExprId; use crate::c_ast::{ - Attribute, CDeclId, CDeclKind, CEnumId, CExprId, CRecordId, CTypedefId, Located, - TypedAstContext, + Attribute, CDeclId, CDeclKind, CEnumId, CRecordId, CTypedefId, Located, TypedAstContext, }; use std::fmt::{self, Debug, Display}; use std::ops::Index; diff --git a/c2rust-transpile/src/c_ast/conversion.rs b/c2rust-transpile/src/c_ast/conversion.rs index 9173d6151f..8266b8fee3 100644 --- a/c2rust-transpile/src/c_ast/conversion.rs +++ b/c2rust-transpile/src/c_ast/conversion.rs @@ -1,8 +1,11 @@ +use crate::c_ast::c_expr::{ + BinOp, CExpr, CExprId, CExprKind, CLiteral, CastKind, ConstIntExpr, Designator, IntBase, + MemberKind, OffsetOfKind, UnOp, UnTypeOp, +}; use crate::c_ast::c_type::{CQualTypeId, CType, CTypeId, CTypeKind, Qualifiers}; use crate::c_ast::{ - AsmOperand, Attribute, BinOp, CDecl, CDeclId, CDeclKind, CExpr, CExprId, CExprKind, CLiteral, - CStmt, CStmtId, CStmtKind, CastKind, ConstIntExpr, Designator, DisplaySrcSpan, IntBase, - MemberKind, OffsetOfKind, TypedAstContext, UnOp, UnTypeOp, + AsmOperand, Attribute, CDecl, CDeclId, CDeclKind, CStmt, CStmtId, CStmtKind, DisplaySrcSpan, + TypedAstContext, }; use crate::diagnostics::diag; use c2rust_ast_exporter::clang_ast::*; diff --git a/c2rust-transpile/src/c_ast/iterators.rs b/c2rust-transpile/src/c_ast/iterators.rs index 2ec3d7d5a0..51275d5862 100644 --- a/c2rust-transpile/src/c_ast/iterators.rs +++ b/c2rust-transpile/src/c_ast/iterators.rs @@ -1,7 +1,6 @@ +use crate::c_ast::c_expr::{CExprId, CExprKind, OffsetOfKind}; use crate::c_ast::c_type::{CTypeId, CTypeKind}; -use crate::c_ast::{ - CDeclId, CDeclKind, CExprId, CExprKind, CStmtId, CStmtKind, OffsetOfKind, TypedAstContext, -}; +use crate::c_ast::{CDeclId, CDeclKind, CStmtId, CStmtKind, TypedAstContext}; #[derive(Copy, Clone, Eq, PartialEq, Debug, Hash)] pub enum SomeId { @@ -40,7 +39,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 @@ -86,7 +85,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 diff --git a/c2rust-transpile/src/c_ast/mod.rs b/c2rust-transpile/src/c_ast/mod.rs index fdc63bc536..6b3e254422 100644 --- a/c2rust-transpile/src/c_ast/mod.rs +++ b/c2rust-transpile/src/c_ast/mod.rs @@ -1,6 +1,6 @@ +use crate::c_ast::c_expr::{BinOp, CExpr, CExprId, CExprKind, ConstIntExpr, UnTypeOp}; use crate::c_ast::c_type::{CFuncTypeId, CQualTypeId, CType, CTypeId, CTypeKind}; use crate::c_ast::iterators::{immediate_children_all_types, DFNodes, NodeVisitor, SomeId}; -use c2rust_ast_exporter::clang_ast::LRValue; use indexmap::{IndexMap, IndexSet}; use itertools::Itertools; use std::cell::RefCell; @@ -16,14 +16,12 @@ pub use self::conversion::*; pub use self::print::Printer; pub use c2rust_ast_exporter::clang_ast::{BuiltinVaListKind, SrcFile, SrcLoc, SrcSpan}; +pub mod c_expr; pub mod c_type; mod conversion; pub mod iterators; mod print; -#[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); @@ -445,116 +443,6 @@ impl TypedAstContext { 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, - } - } - - /// 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)) - } - - /// 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 @@ -590,152 +478,6 @@ impl TypedAstContext { 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); @@ -1166,27 +908,6 @@ impl CommentContext { } } -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; @@ -1212,7 +933,6 @@ impl Index for TypedAstContext { /// All of our AST types should have location information bundled with them pub type CDecl = Located; pub type CStmt = Located; -pub type CExpr = Located; #[derive(Debug, Clone)] pub enum CDeclKind { @@ -1327,527 +1047,6 @@ impl CDeclKind { } } -/// 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 @@ -1927,13 +1126,6 @@ pub struct AsmOperand { pub expression: CExprId, } -#[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 { diff --git a/c2rust-transpile/src/c_ast/print.rs b/c2rust-transpile/src/c_ast/print.rs index e9253d3461..7a9d9a9a5f 100644 --- a/c2rust-transpile/src/c_ast/print.rs +++ b/c2rust-transpile/src/c_ast/print.rs @@ -1,8 +1,6 @@ +use crate::c_ast::c_expr::{BinOp, CExprId, CExprKind, CLiteral, MemberKind, OffsetOfKind, UnOp}; use crate::c_ast::c_type::{CQualTypeId, CTypeId, CTypeKind, Qualifiers}; -use crate::c_ast::{ - BinOp, CDeclId, CDeclKind, CExprId, CExprKind, CLiteral, CStmtId, CStmtKind, MemberKind, - OffsetOfKind, TypedAstContext, UnOp, -}; +use crate::c_ast::{CDeclId, CDeclKind, CStmtId, CStmtKind, TypedAstContext}; use std::io::{Result, Write}; pub struct Printer { diff --git a/c2rust-transpile/src/cfg/mod.rs b/c2rust-transpile/src/cfg/mod.rs index 1b344bd7a8..5d5275ee3a 100644 --- a/c2rust-transpile/src/cfg/mod.rs +++ b/c2rust-transpile/src/cfg/mod.rs @@ -42,10 +42,9 @@ use serde::ser::{ }; use serde_json; +use crate::c_ast::c_expr::{CExprId, CExprKind, ConstIntExpr, UnOp}; use crate::c_ast::c_type::CQualTypeId; -use crate::c_ast::{ - CDeclId, CExprId, CExprKind, CStmtId, CStmtKind, ConstIntExpr, TypedAstContext, UnOp, -}; +use crate::c_ast::{CDeclId, CStmtId, CStmtKind, TypedAstContext}; use crate::translator::*; use crate::with_stmts::WithStmts; use c2rust_ast_builder::mk; diff --git a/c2rust-transpile/src/translator/enums.rs b/c2rust-transpile/src/translator/enums.rs index e88a7a65b6..095cb0ed32 100644 --- a/c2rust-transpile/src/translator/enums.rs +++ b/c2rust-transpile/src/translator/enums.rs @@ -3,10 +3,9 @@ use proc_macro2::Span; use syn::Expr; use crate::c_ast; +use crate::c_ast::c_expr::{CExprId, CExprKind, CLiteral, ConstIntExpr}; use crate::c_ast::c_type::{CQualTypeId, CTypeId, CTypeKind}; -use crate::c_ast::{ - CDeclKind, CEnumConstantId, CEnumId, CExprId, CExprKind, CLiteral, ConstIntExpr, -}; +use crate::c_ast::{CDeclKind, CEnumConstantId, CEnumId}; use crate::diagnostics::TranslationResult; use crate::translator::{signed_int_expr, ConvertedDecl, ExprContext, Translation}; use crate::with_stmts::WithStmts; @@ -113,7 +112,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(_, c_ast::c_expr::UnOp::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 e01496f453..6d5cc52ddf 100644 --- a/c2rust-transpile/src/translator/mod.rs +++ b/c2rust-transpile/src/translator/mod.rs @@ -37,12 +37,14 @@ use c2rust_ast_builder::{mk, properties::*, Builder}; use c2rust_ast_exporter::clang_ast::SrcSpan; use c2rust_ast_printer::pprust; +use crate::c_ast::c_expr::{ + CExprId, CExprKind, CLiteral, CastKind, ConstIntExpr, IntBase, OffsetOfKind, UnTypeOp, +}; use crate::c_ast::c_type::{CQualTypeId, CTypeId, CTypeKind}; use crate::c_ast::iterators::{DFExpr, SomeId}; use crate::c_ast::{ - AsmOperand, CDecl, CDeclId, CDeclKind, CDeclSrcRange, CExprId, CExprKind, CFieldId, CLiteral, - CStmtId, CStmtKind, CastKind, CommentContext, ConstIntExpr, FileId, IntBase, Located, - OffsetOfKind, TypedAstContext, UnTypeOp, + AsmOperand, CDecl, CDeclId, CDeclKind, CDeclSrcRange, CFieldId, CStmtId, CStmtKind, + CommentContext, FileId, Located, TypedAstContext, }; use crate::cfg; use crate::convert_type::TypeConverter; @@ -1716,9 +1718,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::BinOp::{Add, Divide, Modulus, Multiply, Subtract}; + use crate::c_ast::c_expr::CastKind::{IntegralToPointer, PointerToIntegral}; + use crate::c_ast::c_expr::UnOp::{AddressOf, Negate}; let expr_id = match expr_id { Some(expr_id) => expr_id, @@ -2733,31 +2735,31 @@ impl<'c> Translation<'c> { }; match self.ast_context[cond_id].kind { - CExprKind::Binary(_, c_ast::BinOp::EqualEqual, null_expr, ptr, _, _) + CExprKind::Binary(_, c_ast::c_expr::BinOp::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(_, c_ast::c_expr::BinOp::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(_, c_ast::c_expr::BinOp::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(_, c_ast::c_expr::BinOp::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(_, c_ast::c_expr::UnOp::Not, subexpr_id, _) => { self.convert_condition(ctx, !target, subexpr_id) } @@ -3060,7 +3062,7 @@ impl<'c> Translation<'c> { } // ref decayed ptrs generally need a type annotation - if let Some(CExprKind::Unary(_, c_ast::UnOp::AddressOf, _, _)) = + if let Some(CExprKind::Unary(_, c_ast::c_expr::UnOp::AddressOf, _, _)) = initializer_kind { return true; diff --git a/c2rust-transpile/src/translator/operators.rs b/c2rust-transpile/src/translator/operators.rs index 17b4892340..d43813cd64 100644 --- a/c2rust-transpile/src/translator/operators.rs +++ b/c2rust-transpile/src/translator/operators.rs @@ -10,27 +10,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: c_ast::c_expr::BinOp) -> 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()), + c_ast::c_expr::BinOp::Multiply => BinOp::Mul(Default::default()), + c_ast::c_expr::BinOp::Divide => BinOp::Div(Default::default()), + c_ast::c_expr::BinOp::Modulus => BinOp::Rem(Default::default()), + c_ast::c_expr::BinOp::Add => BinOp::Add(Default::default()), + c_ast::c_expr::BinOp::Subtract => BinOp::Sub(Default::default()), + c_ast::c_expr::BinOp::ShiftLeft => BinOp::Shl(Default::default()), + c_ast::c_expr::BinOp::ShiftRight => BinOp::Shr(Default::default()), + c_ast::c_expr::BinOp::Less => BinOp::Lt(Default::default()), + c_ast::c_expr::BinOp::Greater => BinOp::Gt(Default::default()), + c_ast::c_expr::BinOp::LessEqual => BinOp::Le(Default::default()), + c_ast::c_expr::BinOp::GreaterEqual => BinOp::Ge(Default::default()), + c_ast::c_expr::BinOp::EqualEqual => BinOp::Eq(Default::default()), + c_ast::c_expr::BinOp::NotEqual => BinOp::Ne(Default::default()), + c_ast::c_expr::BinOp::BitAnd => BinOp::BitAnd(Default::default()), + c_ast::c_expr::BinOp::BitXor => BinOp::BitXor(Default::default()), + c_ast::c_expr::BinOp::BitOr => BinOp::BitOr(Default::default()), + c_ast::c_expr::BinOp::And => BinOp::And(Default::default()), + c_ast::c_expr::BinOp::Or => BinOp::Or(Default::default()), _ => panic!("C BinOp {:?} is not a valid Rust BinOp", op), } @@ -42,7 +42,7 @@ impl<'c> Translation<'c> { &self, mut ctx: ExprContext, expr_type_id: CQualTypeId, - op: c_ast::BinOp, + op: c_ast::c_expr::BinOp, lhs: CExprId, rhs: CExprId, opt_lhs_type_id: Option, @@ -56,7 +56,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 c_ast::c_expr::BinOp::*; match op { Comma => { // The value of the LHS of a comma expression is always discarded @@ -98,7 +98,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 == c_ast::c_expr::BinOp::EqualEqual || op == c_ast::c_expr::BinOp::NotEqual { ctx = ctx.decay_ref(); } @@ -167,7 +167,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 == c_ast::c_expr::BinOp::Subtract || op == c_ast::c_expr::BinOp::Add { let ty_kind = &self.ast_context.resolve_type(lhs_type_id.ctype).kind; if let CTypeKind::Pointer(_) = ty_kind { @@ -202,7 +202,7 @@ impl<'c> Translation<'c> { &self, ctx: ExprContext, bin_op_kind: BinOp, - bin_op: c_ast::BinOp, + bin_op: c_ast::c_expr::BinOp, read: Box, write: Box, rhs: Box, @@ -273,14 +273,14 @@ impl<'c> Translation<'c> { fn convert_assignment_operator( &self, ctx: ExprContext, - op: c_ast::BinOp, + op: c_ast::c_expr::BinOp, 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 == c_ast::c_expr::BinOp::Assign { assert!(compute_lhs_type_id.is_none()); assert!(compute_res_type_id.is_none()); } else { @@ -309,7 +309,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 c_ast::c_expr::BinOp::*; match op.underlying_assignment() { Some(Add) => neither_ptr, Some(Subtract) => neither_ptr, @@ -352,7 +352,7 @@ impl<'c> Translation<'c> { fn convert_assignment_operator_with_rhs( &self, ctx: ExprContext, - op: c_ast::BinOp, + op: c_ast::c_expr::BinOp, expr_type_id: CQualTypeId, lhs: CExprId, rhs_type_id: CQualTypeId, @@ -360,7 +360,7 @@ impl<'c> Translation<'c> { compute_lhs_type_id: Option, compute_res_type_id: Option, ) -> TranslationResult>> { - if op == c_ast::BinOp::Assign { + if op == c_ast::c_expr::BinOp::Assign { assert!(compute_lhs_type_id.is_none()); assert!(compute_res_type_id.is_none()); } else { @@ -411,11 +411,13 @@ 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(), + c_ast::c_expr::BinOp::AssignAdd + | c_ast::c_expr::BinOp::AssignSubtract + | c_ast::c_expr::BinOp::AssignMultiply + | c_ast::c_expr::BinOp::AssignDivide + | c_ast::c_expr::BinOp::AssignModulus => { + compute_resolved_ty.kind.is_unsigned_integral_type() + } _ => false, }; @@ -440,7 +442,7 @@ impl<'c> Translation<'c> { rvalue: read, }| { // Assignment expression itself - use c_ast::BinOp::*; + use c_ast::c_expr::BinOp::*; let assign_stmt = match op { // Regular (possibly volatile) assignment Assign if !is_volatile => WithStmts::new_val(mk().assign_expr(write, rhs)), @@ -575,7 +577,7 @@ impl<'c> Translation<'c> { fn convert_binary_operator( &self, ctx: ExprContext, - op: c_ast::BinOp, + op: c_ast::c_expr::BinOp, ty: Box, ctype: CTypeId, lhs_type: CQualTypeId, @@ -591,38 +593,52 @@ 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 => { + c_ast::c_expr::BinOp::Add => { + return self.convert_addition(lhs_type, rhs_type, lhs, rhs) + } + c_ast::c_expr::BinOp::Subtract => { return self.convert_subtraction(ty, lhs_type, rhs_type, lhs, rhs) } - c_ast::BinOp::Multiply if is_unsigned_integral_type => { + c_ast::c_expr::BinOp::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), + c_ast::c_expr::BinOp::Multiply => { + mk().binary_expr(BinOp::Mul(Default::default()), lhs, rhs) + } - c_ast::BinOp::Divide if is_unsigned_integral_type => { + c_ast::c_expr::BinOp::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), + c_ast::c_expr::BinOp::Divide => { + mk().binary_expr(BinOp::Div(Default::default()), lhs, rhs) + } - c_ast::BinOp::Modulus if is_unsigned_integral_type => { + c_ast::c_expr::BinOp::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), + c_ast::c_expr::BinOp::Modulus => { + mk().binary_expr(BinOp::Rem(Default::default()), lhs, rhs) + } - c_ast::BinOp::BitXor => mk().binary_expr(BinOp::BitXor(Default::default()), lhs, rhs), + c_ast::c_expr::BinOp::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), + c_ast::c_expr::BinOp::ShiftRight => { + mk().binary_expr(BinOp::Shr(Default::default()), lhs, rhs) + } + c_ast::c_expr::BinOp::ShiftLeft => { + mk().binary_expr(BinOp::Shl(Default::default()), lhs, rhs) + } - c_ast::BinOp::EqualEqual | c_ast::BinOp::NotEqual => { + c_ast::c_expr::BinOp::EqualEqual | c_ast::c_expr::BinOp::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())), + c_ast::c_expr::BinOp::EqualEqual => (true, BinOp::Eq(Default::default())), + c_ast::c_expr::BinOp::NotEqual => (false, BinOp::Ne(Default::default())), _ => unreachable!(), }; let expr = match lhs_rhs_ids { @@ -637,21 +653,25 @@ impl<'c> Translation<'c> { bool_to_int(expr) } - c_ast::BinOp::Less => { + c_ast::c_expr::BinOp::Less => { bool_to_int(mk().binary_expr(BinOp::Lt(Default::default()), lhs, rhs)) } - c_ast::BinOp::Greater => { + c_ast::c_expr::BinOp::Greater => { bool_to_int(mk().binary_expr(BinOp::Gt(Default::default()), lhs, rhs)) } - c_ast::BinOp::GreaterEqual => { + c_ast::c_expr::BinOp::GreaterEqual => { bool_to_int(mk().binary_expr(BinOp::Ge(Default::default()), lhs, rhs)) } - c_ast::BinOp::LessEqual => { + c_ast::c_expr::BinOp::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), + c_ast::c_expr::BinOp::BitAnd => { + mk().binary_expr(BinOp::BitAnd(Default::default()), lhs, rhs) + } + c_ast::c_expr::BinOp::BitOr => { + mk().binary_expr(BinOp::BitOr(Default::default()), lhs, rhs) + } op => unimplemented!("Translation of binary operator {:?}", op), })) @@ -731,9 +751,9 @@ impl<'c> Translation<'c> { arg: CExprId, ) -> TranslationResult>> { let op = if up { - c_ast::BinOp::AssignAdd + c_ast::c_expr::BinOp::AssignAdd } else { - c_ast::BinOp::AssignSubtract + c_ast::c_expr::BinOp::AssignSubtract }; let one = match self.ast_context.resolve_type(ty.ctype).kind { // TODO: If rust gets f16 support: @@ -870,7 +890,7 @@ impl<'c> Translation<'c> { pub fn convert_unary_operator( &self, ctx: ExprContext, - name: c_ast::UnOp, + name: c_ast::c_expr::UnOp, cqual_type: CQualTypeId, arg: CExprId, lrvalue: LRValue, @@ -879,15 +899,23 @@ 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 => { + c_ast::c_expr::UnOp::AddressOf => self.convert_address_of(ctx, cqual_type, arg), + c_ast::c_expr::UnOp::PreIncrement => { + self.convert_pre_increment(ctx, cqual_type, true, arg) + } + c_ast::c_expr::UnOp::PreDecrement => { + self.convert_pre_increment(ctx, cqual_type, false, arg) + } + c_ast::c_expr::UnOp::PostIncrement => { + self.convert_post_increment(ctx, cqual_type, true, arg) + } + c_ast::c_expr::UnOp::PostDecrement => { + self.convert_post_increment(ctx, cqual_type, false, arg) + } + c_ast::c_expr::UnOp::Deref => self.convert_deref(ctx, cqual_type, arg, lrvalue), + c_ast::c_expr::UnOp::Plus => self.convert_expr(ctx.used(), arg, Some(cqual_type)), // promotion is explicit in the clang AST + + c_ast::c_expr::UnOp::Negate => { let val = self.convert_expr(ctx.used(), arg, Some(cqual_type))?; if resolved_ctype.kind.is_unsigned_integral_type() { @@ -896,19 +924,21 @@ impl<'c> Translation<'c> { Ok(val.map(neg_expr)) } } - c_ast::UnOp::Complement => Ok(self + c_ast::c_expr::UnOp::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 => { + c_ast::c_expr::UnOp::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 => { + c_ast::c_expr::UnOp::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 => { + c_ast::c_expr::UnOp::Real + | c_ast::c_expr::UnOp::Imag + | c_ast::c_expr::UnOp::Coawait => { panic!("Unsupported extension operator") } }?; @@ -920,11 +950,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 + c_ast::c_expr::UnOp::PreDecrement + | c_ast::c_expr::UnOp::PreIncrement + | c_ast::c_expr::UnOp::PostDecrement + | c_ast::c_expr::UnOp::PostIncrement + | c_ast::c_expr::UnOp::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 f64111e378..385b81e1f9 100644 --- a/c2rust-transpile/src/translator/pointers.rs +++ b/c2rust-transpile/src/translator/pointers.rs @@ -6,8 +6,8 @@ use failure::{err_msg, format_err}; use syn::{BinOp, Expr, Type, UnOp}; use crate::c_ast; +use crate::c_ast::c_expr::{CExprId, CExprKind, CLiteral, CastKind}; use crate::c_ast::c_type::{CQualTypeId, CTypeId, CTypeKind}; -use crate::c_ast::{CExprId, CExprKind, CLiteral, CastKind}; use crate::diagnostics::{TranslationError, TranslationErrorKind, TranslationResult}; use crate::translator::{ cast_int, format_translation_err, transmute_expr, unwrap_function_pointer, ExprContext, @@ -26,7 +26,7 @@ impl<'c> Translation<'c> { match arg_kind { // C99 6.5.3.2 para 4 - CExprKind::Unary(_, c_ast::UnOp::Deref, target, _) => { + CExprKind::Unary(_, c_ast::c_expr::UnOp::Deref, target, _) => { return self.convert_expr(ctx, *target, None) } // Array subscript functions as a deref too. @@ -211,7 +211,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(_, c_ast::c_expr::UnOp::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 81bb080d1d..43c11c4c6b 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::c_expr::BinOp::{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}; -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::CastKind::{BitCast, IntegralCast}; /// 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 1c00369548..be7c8227b4 100644 --- a/c2rust-transpile/src/translator/structs_unions.rs +++ b/c2rust-transpile/src/translator/structs_unions.rs @@ -7,10 +7,9 @@ use std::ops::Index; use super::named_references::NamedReference; use super::TranslationError; use crate::c_ast; +use crate::c_ast::c_expr::{BinOp, CExprId, CExprKind, MemberKind}; use crate::c_ast::c_type::{CQualTypeId, CTypeId}; -use crate::c_ast::{ - BinOp, CDeclId, CDeclKind, CExprId, CExprKind, CFieldId, CRecordId, MemberKind, -}; +use crate::c_ast::{CDeclId, CDeclKind, CFieldId, CRecordId}; use crate::diagnostics::TranslationResult; use crate::translator::variadic::mk_va_list_ty; use crate::translator::{ConvertedDecl, ExprContext, Translation, PADDING_SUFFIX}; @@ -1033,7 +1032,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(_, c_ast::c_expr::UnOp::AddressOf, subexpr_id, _) = self.ast_context[expr].kind { // Special-case the `(&x)->field` pattern From 3f8969c5509c5ad64f46d206a5c0930e0f615a1d Mon Sep 17 00:00:00 2001 From: Rua Date: Tue, 10 Mar 2026 15:22:05 +0100 Subject: [PATCH 4/8] transpile: Split off c_ast/c_stmt.rs module --- c2rust-transpile/src/c_ast/c_expr.rs | 3 +- c2rust-transpile/src/c_ast/c_stmt.rs | 161 +++++++++++++++++++++++ c2rust-transpile/src/c_ast/conversion.rs | 6 +- c2rust-transpile/src/c_ast/iterators.rs | 5 +- c2rust-transpile/src/c_ast/mod.rs | 154 +--------------------- c2rust-transpile/src/c_ast/print.rs | 3 +- c2rust-transpile/src/cfg/mod.rs | 5 +- c2rust-transpile/src/translator/mod.rs | 5 +- 8 files changed, 178 insertions(+), 164 deletions(-) create mode 100644 c2rust-transpile/src/c_ast/c_stmt.rs diff --git a/c2rust-transpile/src/c_ast/c_expr.rs b/c2rust-transpile/src/c_ast/c_expr.rs index 1fdf66b40d..95001469d7 100644 --- a/c2rust-transpile/src/c_ast/c_expr.rs +++ b/c2rust-transpile/src/c_ast/c_expr.rs @@ -1,5 +1,6 @@ +use crate::c_ast::c_stmt::CStmtId; use crate::c_ast::c_type::{CQualTypeId, CTypeId, CTypeKind}; -use crate::c_ast::{CDeclId, CDeclKind, CFieldId, CStmtId, Located, TypedAstContext}; +use crate::c_ast::{CDeclId, CDeclKind, CFieldId, Located, TypedAstContext}; use c2rust_ast_exporter::clang_ast::LRValue; use std::fmt::{self, Debug, Display}; use std::ops::Index; 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..a45ead0a86 --- /dev/null +++ b/c2rust-transpile/src/c_ast/c_stmt.rs @@ -0,0 +1,161 @@ +use crate::c_ast::c_expr::{CExprId, ConstIntExpr}; +use crate::c_ast::{Attribute, CDeclId, Located, 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 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/conversion.rs b/c2rust-transpile/src/c_ast/conversion.rs index 8266b8fee3..1a02d267c7 100644 --- a/c2rust-transpile/src/c_ast/conversion.rs +++ b/c2rust-transpile/src/c_ast/conversion.rs @@ -2,11 +2,9 @@ use crate::c_ast::c_expr::{ BinOp, CExpr, CExprId, CExprKind, CLiteral, CastKind, ConstIntExpr, Designator, IntBase, MemberKind, OffsetOfKind, UnOp, UnTypeOp, }; +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::{ - AsmOperand, Attribute, CDecl, CDeclId, CDeclKind, CStmt, CStmtId, CStmtKind, DisplaySrcSpan, - TypedAstContext, -}; +use crate::c_ast::{Attribute, CDecl, CDeclId, CDeclKind, DisplaySrcSpan, TypedAstContext}; use crate::diagnostics::diag; use c2rust_ast_exporter::clang_ast::*; use failure::err_msg; diff --git a/c2rust-transpile/src/c_ast/iterators.rs b/c2rust-transpile/src/c_ast/iterators.rs index 51275d5862..2c5e5c1678 100644 --- a/c2rust-transpile/src/c_ast/iterators.rs +++ b/c2rust-transpile/src/c_ast/iterators.rs @@ -1,6 +1,7 @@ use crate::c_ast::c_expr::{CExprId, CExprKind, OffsetOfKind}; +use crate::c_ast::c_stmt::{CStmtId, CStmtKind}; use crate::c_ast::c_type::{CTypeId, CTypeKind}; -use crate::c_ast::{CDeclId, CDeclKind, CStmtId, CStmtKind, TypedAstContext}; +use crate::c_ast::{CDeclId, CDeclKind, TypedAstContext}; #[derive(Copy, Clone, Eq, PartialEq, Debug, Hash)] pub enum SomeId { @@ -191,7 +192,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], diff --git a/c2rust-transpile/src/c_ast/mod.rs b/c2rust-transpile/src/c_ast/mod.rs index 6b3e254422..1e131c0ea2 100644 --- a/c2rust-transpile/src/c_ast/mod.rs +++ b/c2rust-transpile/src/c_ast/mod.rs @@ -1,4 +1,5 @@ use crate::c_ast::c_expr::{BinOp, CExpr, CExprId, CExprKind, ConstIntExpr, UnTypeOp}; +use crate::c_ast::c_stmt::{CLabelId, CStmt, CStmtId}; use crate::c_ast::c_type::{CFuncTypeId, CQualTypeId, CType, CTypeId, CTypeKind}; use crate::c_ast::iterators::{immediate_children_all_types, DFNodes, NodeVisitor, SomeId}; use indexmap::{IndexMap, IndexSet}; @@ -17,6 +18,7 @@ pub use self::print::Printer; pub use c2rust_ast_exporter::clang_ast::{BuiltinVaListKind, SrcFile, SrcLoc, SrcSpan}; pub mod c_expr; +pub mod c_stmt; pub mod c_type; mod conversion; pub mod iterators; @@ -25,11 +27,7 @@ mod print; #[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 CRecordId = CDeclId; // Record types need to point to 'DeclKind::Record' @@ -478,63 +476,6 @@ impl TypedAstContext { panic!("expected a CDeclKind::Function, but passed {func_decl:?}") } - 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. @@ -919,20 +860,8 @@ impl Index for TypedAstContext { } } -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; #[derive(Debug, Clone)] pub enum CDeclKind { @@ -1047,85 +976,6 @@ impl CDeclKind { } } -/// 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, -} - /// Enumeration of supported attributes for Declarations #[derive(Clone, Debug, Eq, Hash, PartialEq)] pub enum Attribute { diff --git a/c2rust-transpile/src/c_ast/print.rs b/c2rust-transpile/src/c_ast/print.rs index 7a9d9a9a5f..636aca44ac 100644 --- a/c2rust-transpile/src/c_ast/print.rs +++ b/c2rust-transpile/src/c_ast/print.rs @@ -1,6 +1,7 @@ use crate::c_ast::c_expr::{BinOp, CExprId, CExprKind, CLiteral, MemberKind, OffsetOfKind, UnOp}; +use crate::c_ast::c_stmt::{CStmtId, CStmtKind}; use crate::c_ast::c_type::{CQualTypeId, CTypeId, CTypeKind, Qualifiers}; -use crate::c_ast::{CDeclId, CDeclKind, CStmtId, CStmtKind, TypedAstContext}; +use crate::c_ast::{CDeclId, CDeclKind, TypedAstContext}; use std::io::{Result, Write}; pub struct Printer { diff --git a/c2rust-transpile/src/cfg/mod.rs b/c2rust-transpile/src/cfg/mod.rs index 5d5275ee3a..9f20b9a8d5 100644 --- a/c2rust-transpile/src/cfg/mod.rs +++ b/c2rust-transpile/src/cfg/mod.rs @@ -15,8 +15,8 @@ //! - convert the `Vec>` back into a `Vec` //! +use crate::c_ast::c_stmt::CLabelId; use crate::c_ast::iterators::{DFExpr, SomeId}; -use crate::c_ast::CLabelId; use crate::diagnostics::TranslationResult; use crate::rust_ast::SpanExt; use c2rust_ast_printer::pprust; @@ -43,8 +43,9 @@ use serde::ser::{ use serde_json; use crate::c_ast::c_expr::{CExprId, CExprKind, ConstIntExpr, UnOp}; +use crate::c_ast::c_stmt::{CStmtId, CStmtKind}; use crate::c_ast::c_type::CQualTypeId; -use crate::c_ast::{CDeclId, CStmtId, CStmtKind, TypedAstContext}; +use crate::c_ast::{CDeclId, TypedAstContext}; use crate::translator::*; use crate::with_stmts::WithStmts; use c2rust_ast_builder::mk; diff --git a/c2rust-transpile/src/translator/mod.rs b/c2rust-transpile/src/translator/mod.rs index 6d5cc52ddf..e86f7ae211 100644 --- a/c2rust-transpile/src/translator/mod.rs +++ b/c2rust-transpile/src/translator/mod.rs @@ -40,11 +40,12 @@ use c2rust_ast_printer::pprust; use crate::c_ast::c_expr::{ CExprId, CExprKind, CLiteral, CastKind, ConstIntExpr, IntBase, OffsetOfKind, UnTypeOp, }; +use crate::c_ast::c_stmt::{AsmOperand, CStmtId, CStmtKind}; use crate::c_ast::c_type::{CQualTypeId, CTypeId, CTypeKind}; use crate::c_ast::iterators::{DFExpr, SomeId}; use crate::c_ast::{ - AsmOperand, CDecl, CDeclId, CDeclKind, CDeclSrcRange, CFieldId, CStmtId, CStmtKind, - CommentContext, FileId, Located, TypedAstContext, + CDecl, CDeclId, CDeclKind, CDeclSrcRange, CFieldId, CommentContext, FileId, Located, + TypedAstContext, }; use crate::cfg; use crate::convert_type::TypeConverter; From 5350500a99b7992a9c2b9eb961781f52a8b6b33c Mon Sep 17 00:00:00 2001 From: Rua Date: Tue, 10 Mar 2026 15:29:10 +0100 Subject: [PATCH 5/8] transpile: Split off c_ast/c_decl.rs module --- c2rust-transpile/src/c_ast/c_decl.rs | 327 +++++++++++++++++ c2rust-transpile/src/c_ast/c_expr.rs | 3 +- c2rust-transpile/src/c_ast/c_stmt.rs | 3 +- c2rust-transpile/src/c_ast/c_type.rs | 5 +- c2rust-transpile/src/c_ast/conversion.rs | 3 +- c2rust-transpile/src/c_ast/iterators.rs | 5 +- c2rust-transpile/src/c_ast/mod.rs | 330 +----------------- c2rust-transpile/src/c_ast/print.rs | 3 +- c2rust-transpile/src/cfg/mod.rs | 3 +- c2rust-transpile/src/convert_type.rs | 4 +- c2rust-transpile/src/translator/comments.rs | 3 +- c2rust-transpile/src/translator/enums.rs | 2 +- c2rust-transpile/src/translator/mod.rs | 6 +- .../src/translator/structs_unions.rs | 2 +- 14 files changed, 357 insertions(+), 342 deletions(-) create mode 100644 c2rust-transpile/src/c_ast/c_decl.rs 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..57c9c2739d --- /dev/null +++ b/c2rust-transpile/src/c_ast/c_decl.rs @@ -0,0 +1,327 @@ +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, 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 { + /// 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 index 95001469d7..0437336d5d 100644 --- a/c2rust-transpile/src/c_ast/c_expr.rs +++ b/c2rust-transpile/src/c_ast/c_expr.rs @@ -1,6 +1,7 @@ +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::{CDeclId, CDeclKind, CFieldId, Located, TypedAstContext}; +use crate::c_ast::{Located, TypedAstContext}; use c2rust_ast_exporter::clang_ast::LRValue; use std::fmt::{self, Debug, Display}; use std::ops::Index; diff --git a/c2rust-transpile/src/c_ast/c_stmt.rs b/c2rust-transpile/src/c_ast/c_stmt.rs index a45ead0a86..d81aad8543 100644 --- a/c2rust-transpile/src/c_ast/c_stmt.rs +++ b/c2rust-transpile/src/c_ast/c_stmt.rs @@ -1,5 +1,6 @@ +use crate::c_ast::c_decl::CDeclId; use crate::c_ast::c_expr::{CExprId, ConstIntExpr}; -use crate::c_ast::{Attribute, CDeclId, Located, TypedAstContext}; +use crate::c_ast::{Attribute, Located, TypedAstContext}; use std::fmt::Debug; use std::ops::Index; diff --git a/c2rust-transpile/src/c_ast/c_type.rs b/c2rust-transpile/src/c_ast/c_type.rs index c71231c89c..6ba5483487 100644 --- a/c2rust-transpile/src/c_ast/c_type.rs +++ b/c2rust-transpile/src/c_ast/c_type.rs @@ -1,7 +1,6 @@ +use crate::c_ast::c_decl::{CDeclId, CDeclKind, CEnumId, CRecordId, CTypedefId}; use crate::c_ast::c_expr::CExprId; -use crate::c_ast::{ - Attribute, CDeclId, CDeclKind, CEnumId, CRecordId, CTypedefId, Located, TypedAstContext, -}; +use crate::c_ast::{Attribute, Located, TypedAstContext}; use std::fmt::{self, Debug, Display}; use std::ops::Index; diff --git a/c2rust-transpile/src/c_ast/conversion.rs b/c2rust-transpile/src/c_ast/conversion.rs index 1a02d267c7..bffacbc884 100644 --- a/c2rust-transpile/src/c_ast/conversion.rs +++ b/c2rust-transpile/src/c_ast/conversion.rs @@ -1,10 +1,11 @@ +use crate::c_ast::c_decl::{CDecl, CDeclId, CDeclKind}; use crate::c_ast::c_expr::{ BinOp, CExpr, CExprId, CExprKind, CLiteral, CastKind, ConstIntExpr, Designator, IntBase, MemberKind, OffsetOfKind, UnOp, UnTypeOp, }; 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::{Attribute, CDecl, CDeclId, CDeclKind, DisplaySrcSpan, TypedAstContext}; +use crate::c_ast::{Attribute, DisplaySrcSpan, TypedAstContext}; use crate::diagnostics::diag; use c2rust_ast_exporter::clang_ast::*; use failure::err_msg; diff --git a/c2rust-transpile/src/c_ast/iterators.rs b/c2rust-transpile/src/c_ast/iterators.rs index 2c5e5c1678..e4ae3cd7db 100644 --- a/c2rust-transpile/src/c_ast/iterators.rs +++ b/c2rust-transpile/src/c_ast/iterators.rs @@ -1,7 +1,8 @@ +use crate::c_ast::c_decl::{CDeclId, CDeclKind}; use crate::c_ast::c_expr::{CExprId, CExprKind, OffsetOfKind}; use crate::c_ast::c_stmt::{CStmtId, CStmtKind}; use crate::c_ast::c_type::{CTypeId, CTypeKind}; -use crate::c_ast::{CDeclId, CDeclKind, TypedAstContext}; +use crate::c_ast::TypedAstContext; #[derive(Copy, Clone, Eq, PartialEq, Debug, Hash)] pub enum SomeId { @@ -147,7 +148,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, diff --git a/c2rust-transpile/src/c_ast/mod.rs b/c2rust-transpile/src/c_ast/mod.rs index 1e131c0ea2..3d831aa651 100644 --- a/c2rust-transpile/src/c_ast/mod.rs +++ b/c2rust-transpile/src/c_ast/mod.rs @@ -1,22 +1,25 @@ -use crate::c_ast::c_expr::{BinOp, CExpr, CExprId, CExprKind, ConstIntExpr, UnTypeOp}; +use crate::c_ast::c_decl::{CDecl, CDeclId, CDeclKind}; +use crate::c_ast::c_expr::{BinOp, CExpr, CExprId, CExprKind, UnTypeOp}; use crate::c_ast::c_stmt::{CLabelId, CStmt, CStmtId}; -use crate::c_ast::c_type::{CFuncTypeId, CQualTypeId, CType, CTypeId, CTypeKind}; +use crate::c_ast::c_type::{CQualTypeId, CType, CTypeId, CTypeKind}; use crate::c_ast::iterators::{immediate_children_all_types, DFNodes, NodeVisitor, SomeId}; -use indexmap::{IndexMap, IndexSet}; +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; @@ -24,17 +27,6 @@ mod conversion; pub mod iterators; mod print; -#[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 - /// AST context containing all of the nodes in the Clang AST #[derive(Debug, Clone, Default)] pub struct TypedAstContext { @@ -168,18 +160,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. @@ -331,151 +311,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) - } - - /// 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 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. @@ -713,30 +548,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(_), - .. - } - ) - } } impl CommentContext { @@ -849,133 +660,6 @@ impl CommentContext { } } -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, - } - } -} - -/// All of our AST types should have location information bundled with them -pub type CDecl = 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, - }) - } -} - /// Enumeration of supported attributes for Declarations #[derive(Clone, Debug, Eq, Hash, PartialEq)] pub enum Attribute { diff --git a/c2rust-transpile/src/c_ast/print.rs b/c2rust-transpile/src/c_ast/print.rs index 636aca44ac..340d31be95 100644 --- a/c2rust-transpile/src/c_ast/print.rs +++ b/c2rust-transpile/src/c_ast/print.rs @@ -1,7 +1,8 @@ +use crate::c_ast::c_decl::{CDeclId, CDeclKind}; use crate::c_ast::c_expr::{BinOp, CExprId, CExprKind, CLiteral, MemberKind, OffsetOfKind, UnOp}; use crate::c_ast::c_stmt::{CStmtId, CStmtKind}; use crate::c_ast::c_type::{CQualTypeId, CTypeId, CTypeKind, Qualifiers}; -use crate::c_ast::{CDeclId, CDeclKind, TypedAstContext}; +use crate::c_ast::TypedAstContext; use std::io::{Result, Write}; pub struct Printer { diff --git a/c2rust-transpile/src/cfg/mod.rs b/c2rust-transpile/src/cfg/mod.rs index 9f20b9a8d5..2147d4a17e 100644 --- a/c2rust-transpile/src/cfg/mod.rs +++ b/c2rust-transpile/src/cfg/mod.rs @@ -42,10 +42,11 @@ use serde::ser::{ }; use serde_json; +use crate::c_ast::c_decl::CDeclId; use crate::c_ast::c_expr::{CExprId, CExprKind, ConstIntExpr, UnOp}; use crate::c_ast::c_stmt::{CStmtId, CStmtKind}; use crate::c_ast::c_type::CQualTypeId; -use crate::c_ast::{CDeclId, TypedAstContext}; +use crate::c_ast::TypedAstContext; use crate::translator::*; use crate::with_stmts::WithStmts; use c2rust_ast_builder::mk; diff --git a/c2rust-transpile/src/convert_type.rs b/c2rust-transpile/src/convert_type.rs index 89c880c3ab..5055a39855 100644 --- a/c2rust-transpile/src/convert_type.rs +++ b/c2rust-transpile/src/convert_type.rs @@ -1,6 +1,6 @@ +use crate::c_ast::c_decl::{CDeclId, CDeclKind, CFieldId, CParamId, CRecordId}; use crate::c_ast::c_type::{CQualTypeId, CTypeId, CTypeKind}; -use crate::c_ast::CDeclId; -use crate::c_ast::{CDeclKind, CFieldId, CParamId, CRecordId, TypedAstContext}; +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/translator/comments.rs b/c2rust-transpile/src/translator/comments.rs index ed9e8ed4bb..e1bfaa226f 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::c_decl::{CDeclId, CDeclKind}; use crate::c_ast::iterators::{immediate_children_all_types, NodeVisitor, SomeId}; -use crate::c_ast::{CDeclId, CDeclKind, CommentContext, SrcLoc, TypedAstContext}; +use crate::c_ast::{CommentContext, 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 095cb0ed32..78a782e616 100644 --- a/c2rust-transpile/src/translator/enums.rs +++ b/c2rust-transpile/src/translator/enums.rs @@ -3,9 +3,9 @@ use proc_macro2::Span; use syn::Expr; use crate::c_ast; +use crate::c_ast::c_decl::{CDeclKind, CEnumConstantId, CEnumId}; use crate::c_ast::c_expr::{CExprId, CExprKind, CLiteral, ConstIntExpr}; use crate::c_ast::c_type::{CQualTypeId, CTypeId, CTypeKind}; -use crate::c_ast::{CDeclKind, CEnumConstantId, CEnumId}; use crate::diagnostics::TranslationResult; use crate::translator::{signed_int_expr, ConvertedDecl, ExprContext, Translation}; use crate::with_stmts::WithStmts; diff --git a/c2rust-transpile/src/translator/mod.rs b/c2rust-transpile/src/translator/mod.rs index e86f7ae211..216a0236e6 100644 --- a/c2rust-transpile/src/translator/mod.rs +++ b/c2rust-transpile/src/translator/mod.rs @@ -37,16 +37,14 @@ use c2rust_ast_builder::{mk, properties::*, Builder}; use c2rust_ast_exporter::clang_ast::SrcSpan; use c2rust_ast_printer::pprust; +use crate::c_ast::c_decl::{CDecl, CDeclId, CDeclKind, CDeclSrcRange, CFieldId}; use crate::c_ast::c_expr::{ CExprId, CExprKind, CLiteral, CastKind, ConstIntExpr, IntBase, OffsetOfKind, UnTypeOp, }; use crate::c_ast::c_stmt::{AsmOperand, CStmtId, CStmtKind}; use crate::c_ast::c_type::{CQualTypeId, CTypeId, CTypeKind}; use crate::c_ast::iterators::{DFExpr, SomeId}; -use crate::c_ast::{ - CDecl, CDeclId, CDeclKind, CDeclSrcRange, CFieldId, CommentContext, FileId, Located, - TypedAstContext, -}; +use crate::c_ast::{CommentContext, FileId, Located, TypedAstContext}; use crate::cfg; use crate::convert_type::TypeConverter; use crate::renamer::Renamer; diff --git a/c2rust-transpile/src/translator/structs_unions.rs b/c2rust-transpile/src/translator/structs_unions.rs index be7c8227b4..edc66b58b0 100644 --- a/c2rust-transpile/src/translator/structs_unions.rs +++ b/c2rust-transpile/src/translator/structs_unions.rs @@ -7,9 +7,9 @@ use std::ops::Index; use super::named_references::NamedReference; use super::TranslationError; use crate::c_ast; +use crate::c_ast::c_decl::{CDeclId, CDeclKind, CFieldId, CRecordId}; use crate::c_ast::c_expr::{BinOp, CExprId, CExprKind, MemberKind}; use crate::c_ast::c_type::{CQualTypeId, CTypeId}; -use crate::c_ast::{CDeclId, CDeclKind, CFieldId, CRecordId}; use crate::diagnostics::TranslationResult; use crate::translator::variadic::mk_va_list_ty; use crate::translator::{ConvertedDecl, ExprContext, Translation, PADDING_SUFFIX}; From 488062cef1cd29e3ed8b81dfc78d9d24156aa398 Mon Sep 17 00:00:00 2001 From: Rua Date: Tue, 10 Mar 2026 15:48:55 +0100 Subject: [PATCH 6/8] transpile: Prefix `c_ast::{BinOp, UnOp, UnTypeOp}` with `C` to disambiguate better --- c2rust-transpile/src/c_ast/c_expr.rs | 62 +++--- c2rust-transpile/src/c_ast/conversion.rs | 98 ++++----- c2rust-transpile/src/c_ast/mod.rs | 12 +- c2rust-transpile/src/c_ast/print.rs | 6 +- c2rust-transpile/src/cfg/mod.rs | 4 +- c2rust-transpile/src/translator/enums.rs | 5 +- c2rust-transpile/src/translator/mod.rs | 26 +-- c2rust-transpile/src/translator/operators.rs | 191 ++++++++---------- c2rust-transpile/src/translator/pointers.rs | 7 +- c2rust-transpile/src/translator/simd.rs | 2 +- .../src/translator/structs_unions.rs | 63 +++--- 11 files changed, 218 insertions(+), 258 deletions(-) diff --git a/c2rust-transpile/src/c_ast/c_expr.rs b/c2rust-transpile/src/c_ast/c_expr.rs index 0437336d5d..db94c1970d 100644 --- a/c2rust-transpile/src/c_ast/c_expr.rs +++ b/c2rust-transpile/src/c_ast/c_expr.rs @@ -25,10 +25,10 @@ pub enum CExprKind { Literal(CQualTypeId, CLiteral), /// Unary operator. - Unary(CQualTypeId, UnOp, CExprId, LRValue), + Unary(CQualTypeId, CUnOp, CExprId, LRValue), /// Unary type operator. - UnaryType(CQualTypeId, UnTypeOp, Option, CQualTypeId), + UnaryType(CQualTypeId, CUnTypeOp, Option, CQualTypeId), /// `offsetof` expression. OffsetOf(CQualTypeId, OffsetOfKind), @@ -36,7 +36,7 @@ pub enum CExprKind { /// Binary operator. Binary( CQualTypeId, - BinOp, + CBinOp, CExprId, CExprId, Option, @@ -250,7 +250,7 @@ pub enum CastKind { /// Represents a unary operator in C (6.5.3 Unary operators) and GNU C extensions #[derive(Debug, Clone, Copy)] -pub enum UnOp { +pub enum CUnOp { AddressOf, // &x Deref, // *x Plus, // +x @@ -267,9 +267,9 @@ pub enum UnOp { Coawait, // [C++ Coroutines] co_await x } -impl UnOp { +impl CUnOp { pub fn as_str(&self) -> &'static str { - use UnOp::*; + use CUnOp::*; match self { AddressOf => "&", Deref => "*", @@ -294,7 +294,7 @@ impl UnOp { ast_context: &TypedAstContext, arg_type: CQualTypeId, ) -> Option { - use UnOp::*; + 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 @@ -325,7 +325,7 @@ impl UnOp { } } -impl Display for UnOp { +impl Display for CUnOp { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.as_str()) } @@ -333,15 +333,15 @@ impl Display for UnOp { /// Represents a unary type operator in C #[derive(Debug, Clone, Copy)] -pub enum UnTypeOp { +pub enum CUnTypeOp { SizeOf, AlignOf, PreferredAlignOf, } -impl UnTypeOp { +impl CUnTypeOp { pub fn as_str(&self) -> &'static str { - use UnTypeOp::*; + use CUnTypeOp::*; match self { SizeOf => "sizeof", AlignOf => "alignof", @@ -350,22 +350,22 @@ impl UnTypeOp { } } -impl Display for UnTypeOp { +impl Display for CUnTypeOp { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.as_str()) } } -impl UnOp { +impl CUnOp { /// Check is the operator is rendered before or after is operand. pub fn is_prefix(&self) -> bool { - !matches!(*self, UnOp::PostIncrement | UnOp::PostDecrement) + !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 BinOp { +pub enum CBinOp { Multiply, // * Divide, // / Modulus, // % @@ -400,9 +400,9 @@ pub enum BinOp { Comma, // , } -impl BinOp { +impl CBinOp { pub fn as_str(&self) -> &'static str { - use BinOp::*; + use CBinOp::*; match self { Multiply => "*", Divide => "/", @@ -442,7 +442,7 @@ impl BinOp { /// Does the rust equivalent of this operator have type (T, T) -> U? #[rustfmt::skip] pub fn input_types_same(&self) -> bool { - use BinOp::*; + use CBinOp::*; self.all_types_same() || matches!(self, Less | Greater | LessEqual | GreaterEqual | EqualEqual | NotEqual | And | Or @@ -455,7 +455,7 @@ impl BinOp { /// 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::*; + use CBinOp::*; matches!( self, Multiply | Divide | Modulus | Add | Subtract | BitAnd | BitXor | BitOr @@ -463,19 +463,19 @@ impl BinOp { } } -impl Display for BinOp { +impl Display for CBinOp { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.as_str()) } } -impl BinOp { +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 BinOp::*; + pub fn underlying_assignment(&self) -> Option { + use CBinOp::*; Some(match *self { AssignAdd => Add, AssignSubtract => Subtract, @@ -566,7 +566,7 @@ impl TypedAstContext { /// 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 { + if let CExprKind::Unary(_, CUnOp::Extension, subexpr, _) = self.index(expr_id).kind { subexpr } else { expr_id @@ -673,11 +673,11 @@ impl TypedAstContext { ShuffleVector(..) | ConvertVector(..) | Call(..) | - Unary(_, UnOp::PreIncrement, _, _) | - Unary(_, UnOp::PostIncrement, _, _) | - Unary(_, UnOp::PreDecrement, _, _) | - Unary(_, UnOp::PostDecrement, _, _) | - Binary(_, BinOp::Assign, _, _, _, _) | + Unary(_, CUnOp::PreIncrement, _, _) | + Unary(_, CUnOp::PostIncrement, _, _) | + Unary(_, CUnOp::PreDecrement, _, _) | + Unary(_, CUnOp::PostDecrement, _, _) | + Binary(_, CBinOp::Assign, _, _, _, _) | InitList { .. } | ImplicitValueInit { .. } | Predefined(..) | @@ -753,8 +753,8 @@ impl TypedAstContext { 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`. + // `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), diff --git a/c2rust-transpile/src/c_ast/conversion.rs b/c2rust-transpile/src/c_ast/conversion.rs index bffacbc884..12eaf8dd78 100644 --- a/c2rust-transpile/src/c_ast/conversion.rs +++ b/c2rust-transpile/src/c_ast/conversion.rs @@ -1,7 +1,7 @@ use crate::c_ast::c_decl::{CDecl, CDeclId, CDeclKind}; use crate::c_ast::c_expr::{ - BinOp, CExpr, CExprId, CExprKind, CLiteral, CastKind, ConstIntExpr, Designator, IntBase, - MemberKind, OffsetOfKind, UnOp, UnTypeOp, + 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}; @@ -1376,30 +1376,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), }; @@ -1505,36 +1505,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!(), }; @@ -1639,9 +1639,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), }; diff --git a/c2rust-transpile/src/c_ast/mod.rs b/c2rust-transpile/src/c_ast/mod.rs index 3d831aa651..2998117c3d 100644 --- a/c2rust-transpile/src/c_ast/mod.rs +++ b/c2rust-transpile/src/c_ast/mod.rs @@ -1,5 +1,5 @@ use crate::c_ast::c_decl::{CDecl, CDeclId, CDeclKind}; -use crate::c_ast::c_expr::{BinOp, CExpr, CExprId, CExprKind, UnTypeOp}; +use crate::c_ast::c_expr::{CBinOp, CExpr, CExprId, CExprKind, CUnTypeOp}; use crate::c_ast::c_stmt::{CLabelId, CStmt, CStmtId}; use crate::c_ast::c_type::{CQualTypeId, CType, CTypeId, CTypeKind}; use crate::c_ast::iterators::{immediate_children_all_types, DFNodes, NodeVisitor, SomeId}; @@ -498,7 +498,7 @@ impl TypedAstContext { } else { Some(rhs_type_id) } - } else if op == BinOp::ShiftLeft || op == BinOp::ShiftRight { + } else if op == CBinOp::ShiftLeft || op == CBinOp::ShiftRight { Some(lhs_type_id) } else { return; @@ -510,11 +510,11 @@ impl TypedAstContext { ), 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`. + // All of these `CUnTypeOp`s should return `size_t`. let kind = match op { - UnTypeOp::SizeOf => CTypeKind::Size, - UnTypeOp::AlignOf => CTypeKind::Size, - UnTypeOp::PreferredAlignOf => CTypeKind::Size, + CUnTypeOp::SizeOf => CTypeKind::Size, + CUnTypeOp::AlignOf => CTypeKind::Size, + CUnTypeOp::PreferredAlignOf => CTypeKind::Size, }; let ty = self .ast_context diff --git a/c2rust-transpile/src/c_ast/print.rs b/c2rust-transpile/src/c_ast/print.rs index 340d31be95..7f750527da 100644 --- a/c2rust-transpile/src/c_ast/print.rs +++ b/c2rust-transpile/src/c_ast/print.rs @@ -1,5 +1,5 @@ use crate::c_ast::c_decl::{CDeclId, CDeclKind}; -use crate::c_ast::c_expr::{BinOp, CExprId, CExprKind, CLiteral, MemberKind, OffsetOfKind, UnOp}; +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; @@ -291,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 2147d4a17e..7f86180d92 100644 --- a/c2rust-transpile/src/cfg/mod.rs +++ b/c2rust-transpile/src/cfg/mod.rs @@ -43,7 +43,7 @@ use serde::ser::{ use serde_json; use crate::c_ast::c_decl::CDeclId; -use crate::c_ast::c_expr::{CExprId, CExprKind, ConstIntExpr, UnOp}; +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; @@ -1787,7 +1787,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/translator/enums.rs b/c2rust-transpile/src/translator/enums.rs index 78a782e616..5d5cec2d2c 100644 --- a/c2rust-transpile/src/translator/enums.rs +++ b/c2rust-transpile/src/translator/enums.rs @@ -2,9 +2,8 @@ use c2rust_ast_builder::mk; use proc_macro2::Span; use syn::Expr; -use crate::c_ast; use crate::c_ast::c_decl::{CDeclKind, CEnumConstantId, CEnumId}; -use crate::c_ast::c_expr::{CExprId, CExprKind, CLiteral, ConstIntExpr}; +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}; @@ -112,7 +111,7 @@ impl<'c> Translation<'c> { return Ok(self.enum_for_i64(enum_type_id, i as i64)); } - CExprKind::Unary(_, c_ast::c_expr::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 216a0236e6..5635f4bc63 100644 --- a/c2rust-transpile/src/translator/mod.rs +++ b/c2rust-transpile/src/translator/mod.rs @@ -39,7 +39,8 @@ use c2rust_ast_printer::pprust; use crate::c_ast::c_decl::{CDecl, CDeclId, CDeclKind, CDeclSrcRange, CFieldId}; use crate::c_ast::c_expr::{ - CExprId, CExprKind, CLiteral, CastKind, ConstIntExpr, IntBase, OffsetOfKind, UnTypeOp, + 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}; @@ -1717,9 +1718,9 @@ impl<'c> Translation<'c> { expr_id: Option, qtype: CQualTypeId, ) -> bool { - use crate::c_ast::c_expr::BinOp::{Add, Divide, Modulus, Multiply, Subtract}; + 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}; - use crate::c_ast::c_expr::UnOp::{AddressOf, Negate}; let expr_id = match expr_id { Some(expr_id) => expr_id, @@ -2734,31 +2735,31 @@ impl<'c> Translation<'c> { }; match self.ast_context[cond_id].kind { - CExprKind::Binary(_, c_ast::c_expr::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::c_expr::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::c_expr::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::c_expr::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::c_expr::UnOp::Not, subexpr_id, _) => { + CExprKind::Unary(_, CUnOp::Not, subexpr_id, _) => { self.convert_condition(ctx, !target, subexpr_id) } @@ -3061,8 +3062,7 @@ impl<'c> Translation<'c> { } // ref decayed ptrs generally need a type annotation - if let Some(CExprKind::Unary(_, c_ast::c_expr::UnOp::AddressOf, _, _)) = - initializer_kind + if let Some(CExprKind::Unary(_, CUnOp::AddressOf, _, _)) = initializer_kind { return true; } @@ -3498,7 +3498,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); @@ -3514,10 +3514,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 d43813cd64..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::c_expr::BinOp) -> Self { +impl From for BinOp { + fn from(op: CBinOp) -> Self { match op { - c_ast::c_expr::BinOp::Multiply => BinOp::Mul(Default::default()), - c_ast::c_expr::BinOp::Divide => BinOp::Div(Default::default()), - c_ast::c_expr::BinOp::Modulus => BinOp::Rem(Default::default()), - c_ast::c_expr::BinOp::Add => BinOp::Add(Default::default()), - c_ast::c_expr::BinOp::Subtract => BinOp::Sub(Default::default()), - c_ast::c_expr::BinOp::ShiftLeft => BinOp::Shl(Default::default()), - c_ast::c_expr::BinOp::ShiftRight => BinOp::Shr(Default::default()), - c_ast::c_expr::BinOp::Less => BinOp::Lt(Default::default()), - c_ast::c_expr::BinOp::Greater => BinOp::Gt(Default::default()), - c_ast::c_expr::BinOp::LessEqual => BinOp::Le(Default::default()), - c_ast::c_expr::BinOp::GreaterEqual => BinOp::Ge(Default::default()), - c_ast::c_expr::BinOp::EqualEqual => BinOp::Eq(Default::default()), - c_ast::c_expr::BinOp::NotEqual => BinOp::Ne(Default::default()), - c_ast::c_expr::BinOp::BitAnd => BinOp::BitAnd(Default::default()), - c_ast::c_expr::BinOp::BitXor => BinOp::BitXor(Default::default()), - c_ast::c_expr::BinOp::BitOr => BinOp::BitOr(Default::default()), - c_ast::c_expr::BinOp::And => BinOp::And(Default::default()), - c_ast::c_expr::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::c_expr::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::c_expr::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::c_expr::BinOp::EqualEqual || op == c_ast::c_expr::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::c_expr::BinOp::Subtract || op == c_ast::c_expr::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::c_expr::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::c_expr::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::c_expr::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::c_expr::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::c_expr::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::c_expr::BinOp::Assign { + if op == CBinOp::Assign { assert!(compute_lhs_type_id.is_none()); assert!(compute_res_type_id.is_none()); } else { @@ -411,13 +412,11 @@ impl<'c> Translation<'c> { }; let is_unsigned_arith = match op { - c_ast::c_expr::BinOp::AssignAdd - | c_ast::c_expr::BinOp::AssignSubtract - | c_ast::c_expr::BinOp::AssignMultiply - | c_ast::c_expr::BinOp::AssignDivide - | c_ast::c_expr::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, }; @@ -442,7 +441,7 @@ impl<'c> Translation<'c> { rvalue: read, }| { // Assignment expression itself - use c_ast::c_expr::BinOp::*; + use CBinOp::*; let assign_stmt = match op { // Regular (possibly volatile) assignment Assign if !is_volatile => WithStmts::new_val(mk().assign_expr(write, rhs)), @@ -577,7 +576,7 @@ impl<'c> Translation<'c> { fn convert_binary_operator( &self, ctx: ExprContext, - op: c_ast::c_expr::BinOp, + op: CBinOp, ty: Box, ctype: CTypeId, lhs_type: CQualTypeId, @@ -593,52 +592,36 @@ impl<'c> Translation<'c> { .is_unsigned_integral_type(); Ok(WithStmts::new_val(match op { - c_ast::c_expr::BinOp::Add => { - return self.convert_addition(lhs_type, rhs_type, lhs, rhs) - } - c_ast::c_expr::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::c_expr::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::c_expr::BinOp::Multiply => { - mk().binary_expr(BinOp::Mul(Default::default()), lhs, rhs) - } + CBinOp::Multiply => mk().binary_expr(BinOp::Mul(Default::default()), lhs, rhs), - c_ast::c_expr::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::c_expr::BinOp::Divide => { - mk().binary_expr(BinOp::Div(Default::default()), lhs, rhs) - } + CBinOp::Divide => mk().binary_expr(BinOp::Div(Default::default()), lhs, rhs), - c_ast::c_expr::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::c_expr::BinOp::Modulus => { - mk().binary_expr(BinOp::Rem(Default::default()), lhs, rhs) - } + CBinOp::Modulus => mk().binary_expr(BinOp::Rem(Default::default()), lhs, rhs), - c_ast::c_expr::BinOp::BitXor => { - mk().binary_expr(BinOp::BitXor(Default::default()), lhs, rhs) - } + CBinOp::BitXor => mk().binary_expr(BinOp::BitXor(Default::default()), lhs, rhs), - c_ast::c_expr::BinOp::ShiftRight => { - mk().binary_expr(BinOp::Shr(Default::default()), lhs, rhs) - } - c_ast::c_expr::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::c_expr::BinOp::EqualEqual | c_ast::c_expr::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::c_expr::BinOp::EqualEqual => (true, BinOp::Eq(Default::default())), - c_ast::c_expr::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 { @@ -653,25 +636,19 @@ impl<'c> Translation<'c> { bool_to_int(expr) } - c_ast::c_expr::BinOp::Less => { - bool_to_int(mk().binary_expr(BinOp::Lt(Default::default()), lhs, rhs)) - } - c_ast::c_expr::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::c_expr::BinOp::GreaterEqual => { + CBinOp::GreaterEqual => { bool_to_int(mk().binary_expr(BinOp::Ge(Default::default()), lhs, rhs)) } - c_ast::c_expr::BinOp::LessEqual => { + CBinOp::LessEqual => { bool_to_int(mk().binary_expr(BinOp::Le(Default::default()), lhs, rhs)) } - c_ast::c_expr::BinOp::BitAnd => { - mk().binary_expr(BinOp::BitAnd(Default::default()), lhs, rhs) - } - c_ast::c_expr::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), })) @@ -751,9 +728,9 @@ impl<'c> Translation<'c> { arg: CExprId, ) -> TranslationResult>> { let op = if up { - c_ast::c_expr::BinOp::AssignAdd + CBinOp::AssignAdd } else { - c_ast::c_expr::BinOp::AssignSubtract + CBinOp::AssignSubtract }; let one = match self.ast_context.resolve_type(ty.ctype).kind { // TODO: If rust gets f16 support: @@ -890,7 +867,7 @@ impl<'c> Translation<'c> { pub fn convert_unary_operator( &self, ctx: ExprContext, - name: c_ast::c_expr::UnOp, + name: CUnOp, cqual_type: CQualTypeId, arg: CExprId, lrvalue: LRValue, @@ -899,23 +876,15 @@ impl<'c> Translation<'c> { let resolved_ctype = self.ast_context.resolve_type(ctype); let mut unary = match name { - c_ast::c_expr::UnOp::AddressOf => self.convert_address_of(ctx, cqual_type, arg), - c_ast::c_expr::UnOp::PreIncrement => { - self.convert_pre_increment(ctx, cqual_type, true, arg) - } - c_ast::c_expr::UnOp::PreDecrement => { - self.convert_pre_increment(ctx, cqual_type, false, arg) - } - c_ast::c_expr::UnOp::PostIncrement => { - self.convert_post_increment(ctx, cqual_type, true, arg) - } - c_ast::c_expr::UnOp::PostDecrement => { - self.convert_post_increment(ctx, cqual_type, false, arg) - } - c_ast::c_expr::UnOp::Deref => self.convert_deref(ctx, cqual_type, arg, lrvalue), - c_ast::c_expr::UnOp::Plus => self.convert_expr(ctx.used(), arg, Some(cqual_type)), // promotion is explicit in the clang AST - - c_ast::c_expr::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() { @@ -924,21 +893,19 @@ impl<'c> Translation<'c> { Ok(val.map(neg_expr)) } } - c_ast::c_expr::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::c_expr::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::c_expr::UnOp::Extension => { + CUnOp::Extension => { let arg = self.convert_expr(ctx, arg, Some(cqual_type))?; Ok(arg) } - c_ast::c_expr::UnOp::Real - | c_ast::c_expr::UnOp::Imag - | c_ast::c_expr::UnOp::Coawait => { + CUnOp::Real | CUnOp::Imag | CUnOp::Coawait => { panic!("Unsupported extension operator") } }?; @@ -950,11 +917,11 @@ impl<'c> Translation<'c> { // it's a no-op around the inner expression. if !matches!( name, - c_ast::c_expr::UnOp::PreDecrement - | c_ast::c_expr::UnOp::PreIncrement - | c_ast::c_expr::UnOp::PostDecrement - | c_ast::c_expr::UnOp::PostIncrement - | c_ast::c_expr::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 385b81e1f9..b31d90ffc1 100644 --- a/c2rust-transpile/src/translator/pointers.rs +++ b/c2rust-transpile/src/translator/pointers.rs @@ -5,8 +5,7 @@ use c2rust_ast_exporter::clang_ast::LRValue; use failure::{err_msg, format_err}; use syn::{BinOp, Expr, Type, UnOp}; -use crate::c_ast; -use crate::c_ast::c_expr::{CExprId, CExprKind, CLiteral, CastKind}; +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::{ @@ -26,7 +25,7 @@ impl<'c> Translation<'c> { match arg_kind { // C99 6.5.3.2 para 4 - CExprKind::Unary(_, c_ast::c_expr::UnOp::Deref, target, _) => { + CExprKind::Unary(_, CUnOp::Deref, target, _) => { return self.convert_expr(ctx, *target, None) } // Array subscript functions as a deref too. @@ -211,7 +210,7 @@ impl<'c> Translation<'c> { ) -> TranslationResult>> { let arg_expr_kind = &self.ast_context.index(arg).kind; - if let &CExprKind::Unary(_, c_ast::c_expr::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 43c11c4c6b..747ff60214 100644 --- a/c2rust-transpile/src/translator/simd.rs +++ b/c2rust-transpile/src/translator/simd.rs @@ -3,7 +3,7 @@ use super::*; -use crate::c_ast::c_expr::BinOp::{Add, BitAnd, ShiftRight}; +use crate::c_ast::c_expr::CBinOp::{Add, BitAnd, ShiftRight}; use crate::c_ast::c_expr::CExprKind::{ Binary, Call, Conditional, ExplicitCast, ImplicitCast, Literal, }; diff --git a/c2rust-transpile/src/translator/structs_unions.rs b/c2rust-transpile/src/translator/structs_unions.rs index edc66b58b0..0f26afbaa3 100644 --- a/c2rust-transpile/src/translator/structs_unions.rs +++ b/c2rust-transpile/src/translator/structs_unions.rs @@ -6,9 +6,8 @@ use std::ops::Index; use super::named_references::NamedReference; use super::TranslationError; -use crate::c_ast; use crate::c_ast::c_decl::{CDeclId, CDeclKind, CFieldId, CRecordId}; -use crate::c_ast::c_expr::{BinOp, CExprId, CExprKind, MemberKind}; +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; @@ -19,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}; @@ -150,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"]) @@ -711,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, @@ -732,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) } - BinOp::AssignSubtract => { - mk().binary_expr(RBinOp::Sub(Default::default()), lhs_expr_read, rhs_expr) + CBinOp::AssignSubtract => { + mk().binary_expr(BinOp::Sub(Default::default()), lhs_expr_read, rhs_expr) } - BinOp::AssignMultiply => { - mk().binary_expr(RBinOp::Mul(Default::default()), lhs_expr_read, rhs_expr) + CBinOp::AssignMultiply => { + mk().binary_expr(BinOp::Mul(Default::default()), lhs_expr_read, rhs_expr) } - BinOp::AssignDivide => { - mk().binary_expr(RBinOp::Div(Default::default()), lhs_expr_read, rhs_expr) + CBinOp::AssignDivide => { + mk().binary_expr(BinOp::Div(Default::default()), lhs_expr_read, rhs_expr) } - BinOp::AssignModulus => { - mk().binary_expr(RBinOp::Rem(Default::default()), lhs_expr_read, rhs_expr) + CBinOp::AssignModulus => { + mk().binary_expr(BinOp::Rem(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::AssignBitXor => { + mk().binary_expr(BinOp::BitXor(Default::default()), lhs_expr_read, rhs_expr) } - BinOp::AssignShiftRight => { - mk().binary_expr(RBinOp::Shr(Default::default()), lhs_expr_read, rhs_expr) + CBinOp::AssignShiftLeft => { + mk().binary_expr(BinOp::Shl(Default::default()), lhs_expr_read, rhs_expr) } - BinOp::AssignBitOr => { - mk().binary_expr(RBinOp::BitOr(Default::default()), lhs_expr_read, rhs_expr) + CBinOp::AssignShiftRight => { + mk().binary_expr(BinOp::Shr(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::AssignBitOr => { + mk().binary_expr(BinOp::BitOr(Default::default()), lhs_expr_read, 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"), }; @@ -1032,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::c_expr::UnOp::AddressOf, subexpr_id, _) = + if let CExprKind::Unary(_, CUnOp::AddressOf, subexpr_id, _) = self.ast_context[expr].kind { // Special-case the `(&x)->field` pattern From a9afc7c78208534d34e308f6ade5efd3ba2a2031 Mon Sep 17 00:00:00 2001 From: Rua Date: Tue, 10 Mar 2026 16:41:23 +0100 Subject: [PATCH 7/8] transpile: Fix documentation typo in c_expr.rs --- c2rust-transpile/src/c_ast/c_expr.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/c2rust-transpile/src/c_ast/c_expr.rs b/c2rust-transpile/src/c_ast/c_expr.rs index db94c1970d..6c3ee46f62 100644 --- a/c2rust-transpile/src/c_ast/c_expr.rs +++ b/c2rust-transpile/src/c_ast/c_expr.rs @@ -357,7 +357,7 @@ impl Display for CUnTypeOp { } impl CUnOp { - /// Check is the operator is rendered before or after is operand. + /// Check if the operator is rendered before or after its operand. pub fn is_prefix(&self) -> bool { !matches!(*self, CUnOp::PostIncrement | CUnOp::PostDecrement) } From 391aafdc84458fdd5e6a3771fae0d13bb826718b Mon Sep 17 00:00:00 2001 From: Rua Date: Sun, 22 Mar 2026 20:46:44 +0100 Subject: [PATCH 8/8] transpile: Move `bubble_expr_types` to conversion.rs --- c2rust-transpile/src/c_ast/conversion.rs | 111 +++++++++++++++++++++++ c2rust-transpile/src/c_ast/mod.rs | 111 +---------------------- c2rust-transpile/src/translator/mod.rs | 5 - 3 files changed, 114 insertions(+), 113 deletions(-) diff --git a/c2rust-transpile/src/c_ast/conversion.rs b/c2rust-transpile/src/c_ast/conversion.rs index 12eaf8dd78..616645ca91 100644 --- a/c2rust-transpile/src/c_ast/conversion.rs +++ b/c2rust-transpile/src/c_ast/conversion.rs @@ -5,6 +5,7 @@ use crate::c_ast::c_expr::{ }; 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, SomeId}; use crate::c_ast::{Attribute, DisplaySrcSpan, TypedAstContext}; use crate::diagnostics::diag; use c2rust_ast_exporter::clang_ast::*; @@ -564,6 +565,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. diff --git a/c2rust-transpile/src/c_ast/mod.rs b/c2rust-transpile/src/c_ast/mod.rs index 2998117c3d..47936dd524 100644 --- a/c2rust-transpile/src/c_ast/mod.rs +++ b/c2rust-transpile/src/c_ast/mod.rs @@ -1,8 +1,8 @@ use crate::c_ast::c_decl::{CDecl, CDeclId, CDeclKind}; -use crate::c_ast::c_expr::{CBinOp, CExpr, CExprId, CExprKind, CUnTypeOp}; +use crate::c_ast::c_expr::{CExpr, CExprId, CExprKind}; use crate::c_ast::c_stmt::{CLabelId, CStmt, CStmtId}; -use crate::c_ast::c_type::{CQualTypeId, CType, CTypeId, CTypeKind}; -use crate::c_ast::iterators::{immediate_children_all_types, DFNodes, NodeVisitor, SomeId}; +use crate::c_ast::c_type::{CType, CTypeId, CTypeKind}; +use crate::c_ast::iterators::{DFNodes, SomeId}; use indexmap::IndexMap; use itertools::Itertools; use std::cell::RefCell; @@ -435,111 +435,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 == 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.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. diff --git a/c2rust-transpile/src/translator/mod.rs b/c2rust-transpile/src/translator/mod.rs index 5635f4bc63..af48ecadc1 100644 --- a/c2rust-transpile/src/translator/mod.rs +++ b/c2rust-transpile/src/translator/mod.rs @@ -725,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),