Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 58 additions & 0 deletions c2rust-transpile/src/translator/literals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,64 @@ impl<'c> Translation<'c> {
bytes_padded
}

/// Convert a C compound literal expression to a Rust expression.
pub fn convert_compound_literal(
&self,
ctx: ExprContext,
qty: CQualTypeId,
val: CExprId,
override_ty: Option<CQualTypeId>,
) -> TranslationResult<WithStmts<Box<Expr>>> {
// C compound literals are lvalues, but equivalent Rust expressions generally are not.
// So if an address is needed, store it in an intermediate variable first.
if !ctx.needs_address() || ctx.expanding_macro.is_some() {
return self.convert_expr(ctx, val, override_ty);
}

let fresh_name = self.renamer.borrow_mut().fresh();
let fresh_ty = self.convert_type(override_ty.unwrap_or(qty).ctype)?;

// Translate the expression to be assigned to the fresh variable.
// It will be assigned by value, so we don't need its address anymore.
let val = self.convert_expr(ctx.set_needs_address(false), val, override_ty)?;

val.and_then(|val| {
// If we are translating a static variable,
// then the fresh variable should also be static.
if ctx.is_static {
let item = mk().mutbl().static_item(&fresh_name, fresh_ty, val);
let fresh_stmt = mk().item_stmt(item);
let mut val = WithStmts::new(vec![fresh_stmt], mk().ident_expr(fresh_name));

// Accessing a static variable is unsafe.
// In the current nightly, this applies also to taking a raw pointer,
// but this requirement was removed in later versions of the
// `raw_ref_op` feature.
if self.tcfg.edition < Edition2024 {
val.set_unsafe();
}

Ok(val)
} else {
let mutbl = if qty.qualifiers.is_const {
Mutability::Immutable
} else {
Mutability::Mutable
};
let local = mk().local(
mk().set_mutbl(mutbl).ident_pat(&fresh_name),
Some(fresh_ty),
Some(val),
);
let fresh_stmt = mk().local_stmt(Box::new(local));
Ok(WithStmts::new(
vec![fresh_stmt],
mk().ident_expr(fresh_name),
))
}
})
}

/// Convert an initialization list into an expression. These initialization lists can be
/// used as array literals, struct literals, and union literals in code.
pub fn convert_init_list(
Expand Down
227 changes: 105 additions & 122 deletions c2rust-transpile/src/translator/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1685,7 +1685,6 @@ impl<'c> Translation<'c> {

use CExprKind::*;
match self.ast_context[expr_id].kind {
DeclRef(_, _, LRValue::LValue) => return true,
ImplicitCast(_, _, cast_kind, _, _) | ExplicitCast(_, _, cast_kind, _, _) => {
use CastKind::*;
match cast_kind {
Expand Down Expand Up @@ -2143,21 +2142,47 @@ impl<'c> Translation<'c> {
ref attrs,
..
} if has_static_duration || has_thread_duration => {
if has_thread_duration {
self.use_feature("thread_local");
}

let new_name = &self
.renamer
.borrow()
.get(&decl_id)
.expect("Variables should already be renamed");

let mut static_def = if is_externally_visible {
mk_linkage(false, new_name, ident, self.tcfg.edition)
.pub_()
.extern_("C")
} else if self.cur_file.get().is_some() {
mk().pub_()
} else {
mk()
};

// Force mutability due to the potential for raw pointers occurring in the type
// and because we may be assigning to these variables in the external initializer
static_def = static_def.mutbl();

if has_thread_duration {
self.use_feature("thread_local");
static_def = static_def.single_attr("thread_local");
}

// Add static attributes
for attr in attrs {
static_def = match attr {
c_ast::Attribute::Used => static_def.single_attr("used"),
c_ast::Attribute::Section(name) => {
static_def.str_attr("link_section", name)
}
_ => continue,
};
}

let ctx = ctx.static_();

// Collect problematic static initializers and offload them to sections for the linker
// to initialize for us
let (ty, init) = if self.static_initializer_is_uncompilable(initializer, typ) {
if self.static_initializer_is_uncompilable(initializer, typ) {
// Note: We don't pass `is_const` through here. Extracted initializers are run
// outside of the static initializer, in a non-const context.
let ctx = ctx.not_const();
Expand Down Expand Up @@ -2186,55 +2211,28 @@ impl<'c> Translation<'c> {

self.add_static_initializer_to_section(ctx, new_name, typ, &mut init)?;

(ty, init)
Ok(ConvertedDecl::Item(
static_def.span(span).static_item(new_name, ty, init),
))
} else {
let ConvertedVariable { ty, mutbl: _, init } =
self.convert_variable(ctx.const_(), initializer, typ)?;
let mut init = init?;
let mut items = init.stmts_to_items().ok_or_else(|| {
format_err!("Expected only item statements in static initializer")
})?;

// TODO: Replace this by relying entirely on
// WithStmts.is_unsafe() of the translated variable
if self.static_initializer_is_unsafe(initializer, typ) {
init.set_unsafe()
}
let init = init.to_unsafe_pure_expr().ok_or_else(|| {
format_err!("Expected no side-effects in static initializer")
})?;

(ty, init)
};
let init = init.to_unsafe_pure_expr().unwrap();
let item = static_def.span(span).static_item(new_name, ty, init);
items.push(item);

let static_def = if is_externally_visible {
mk_linkage(false, new_name, ident, self.tcfg.edition)
.pub_()
.extern_("C")
} else if self.cur_file.get().is_some() {
mk().pub_()
} else {
mk()
};

// Force mutability due to the potential for raw pointers occurring in the type
// and because we may be assigning to these variables in the external initializer
let mut static_def = static_def.span(span).mutbl();
if has_thread_duration {
static_def = static_def.single_attr("thread_local");
}

// Add static attributes
for attr in attrs {
static_def = match attr {
c_ast::Attribute::Used => static_def.single_attr("used"),
c_ast::Attribute::Section(name) => static_def
.unsafety(attr_unsafety(self.tcfg.edition))
.str_attr("link_section", name)
.unsafety(Unsafety::Normal),
_ => continue,
}
Ok(ConvertedDecl::Items(items))
}

Ok(ConvertedDecl::Item(
static_def.static_item(new_name, ty, init),
))
}

Variable { .. } => Err(TranslationError::generic(
Expand Down Expand Up @@ -3577,52 +3575,73 @@ impl<'c> Translation<'c> {
val = self.volatile_read(val, qual_ty)?;
}

// If the variable is actually an `EnumConstant`, we need to add a cast to the
// expected integral type.
if let &CDeclKind::EnumConstant { .. } = decl {
val = self.convert_cast_from_enum(qual_ty.ctype, val)?;
}

// If we are referring to a function and need its address, we
// need to cast it to fn() to ensure that it has a real address.
let mut set_unsafe = false;
if ctx.needs_address() {
if let CDeclKind::Function { parameters, .. } = decl {
let ty = self.convert_type(qual_ty.ctype)?;
let actual_ty = self
.type_converter
.borrow_mut()
.knr_function_type_with_parameters(
&self.ast_context,
qual_ty.ctype,
parameters,
)?;
if let Some(actual_ty) = actual_ty {
if actual_ty != ty {
// If we're casting a concrete function to
// a K&R function pointer type, use transmute
self.import_type(qual_ty.ctype);

val = transmute_expr(actual_ty, ty, val);
set_unsafe = true;
}
} else {
let decl_kind = &self.ast_context[decl_id].kind;
let kind_with_declared_args =
self.ast_context.fn_decl_ty_with_declared_args(decl_kind);

if let Some(ty) = self
.ast_context
.type_for_kind(&kind_with_declared_args)
.map(CQualTypeId::new)
{
let ty = self.convert_type(ty.ctype)?;
val = mk().cast_expr(val, ty);

match decl {
CDeclKind::EnumConstant { .. } => {
// If the variable is actually an `EnumConstant`, we need to add a cast to
// the expected integral type.
val = self.convert_cast_from_enum(qual_ty.ctype, val)?;
}

CDeclKind::Function { parameters, .. } => {
// If we are referring to a function and need its address, we
// need to cast it to fn() to ensure that it has a real address.
if ctx.needs_address() {
let ty = self.convert_type(qual_ty.ctype)?;
let actual_ty = self
.type_converter
.borrow_mut()
.knr_function_type_with_parameters(
&self.ast_context,
qual_ty.ctype,
parameters,
)?;
if let Some(actual_ty) = actual_ty {
if actual_ty != ty {
// If we're casting a concrete function to
// a K&R function pointer type, use transmute
self.import_type(qual_ty.ctype);

val = transmute_expr(actual_ty, ty, val);
set_unsafe = true;
}
} else {
val = mk().cast_expr(val, ty);
let decl_kind = &self.ast_context[decl_id].kind;
let kind_with_declared_args =
self.ast_context.fn_decl_ty_with_declared_args(decl_kind);

if let Some(ty) = self
.ast_context
.type_for_kind(&kind_with_declared_args)
.map(CQualTypeId::new)
{
let ty = self.convert_type(ty.ctype)?;
val = mk().cast_expr(val, ty);
} else {
val = mk().cast_expr(val, ty);
}
}
}
}

CDeclKind::Variable {
has_static_duration,
has_thread_duration,
..
} => {
// Accessing a static variable is unsafe.
// In the current nightly, this applies also to taking a raw pointer,
// but this requirement was removed in later versions of the
// `raw_ref_op` feature.
if (*has_static_duration || *has_thread_duration)
&& (self.tcfg.edition < Edition2024 || !ctx.needs_address())
{
set_unsafe = true;
}
}

_ => {}
}

if let CTypeKind::VariableArray(..) =
Expand Down Expand Up @@ -4013,43 +4032,7 @@ impl<'c> Translation<'c> {

Paren(_, val) => self.convert_expr(ctx, val, override_ty),

CompoundLiteral(qty, val) => {
if !ctx.needs_address() || ctx.is_const {
// consts have their intermediates' lifetimes extended.
return self.convert_expr(ctx, val, override_ty);
}

// C compound literals are lvalues, but equivalent Rust expressions generally are not.
// So if an address is needed, store it in an intermediate variable first.
let fresh_name = self.renamer.borrow_mut().fresh();
let fresh_ty = self.convert_type(override_ty.unwrap_or(qty).ctype)?;

// Translate the expression to be assigned to the fresh variable.
// It will be assigned by value, so we don't need its address anymore.
let val = self.convert_expr(ctx.set_needs_address(false), val, override_ty)?;

val.and_then(|val| {
let fresh_stmt = {
let mutbl = if qty.qualifiers.is_const {
Mutability::Immutable
} else {
Mutability::Mutable
};

let local = mk().local(
mk().set_mutbl(mutbl).ident_pat(&fresh_name),
Some(fresh_ty),
Some(val),
);
mk().local_stmt(Box::new(local))
};

Ok(WithStmts::new(
vec![fresh_stmt],
mk().ident_expr(fresh_name),
))
})
}
CompoundLiteral(qty, val) => self.convert_compound_literal(ctx, qty, val, override_ty),

InitList(ty, ref ids, opt_union_field_id, _) => {
self.convert_init_list(ctx, ty, ids, opt_union_field_id)
Expand Down
6 changes: 1 addition & 5 deletions c2rust-transpile/src/translator/pointers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,11 +144,7 @@ impl<'c> Translation<'c> {
// and must be regular-borrowed first.
// Borrowing in a const context will extend the lifetime to static.
else if arg_is_macro
|| ctx.is_const
&& matches!(
arg_expr_kind,
Some(CExprKind::Literal(..) | CExprKind::CompoundLiteral(..))
)
|| ctx.is_const && matches!(arg_expr_kind, Some(CExprKind::Literal(..)))
{
let arg_cty_kind = &self.ast_context.resolve_type(arg_cty.ctype).kind;

Expand Down
Loading
Loading