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
13 changes: 13 additions & 0 deletions c2rust-transpile/src/translator/enums.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,19 @@ impl<'c> Translation<'c> {
WithStmts::new_val(self.enum_for_i64(type_id, 0))
}

/// Translates a `DeclRef` for an `EnumConstant`.
pub fn convert_enum_constant_decl_ref(
&self,
expr_type_id: CQualTypeId,
enum_constant_id: CEnumConstantId,
) -> TranslationResult<WithStmts<Box<Expr>>> {
let val = self.enum_constant_expr(enum_constant_id);

// Add a cast to the expected integral type.
self.convert_cast_from_enum(expr_type_id.ctype, val)
.map(WithStmts::new_val)
}

/// Translate a cast where the source type, but not the target type, is an `enum` type.
pub fn convert_cast_from_enum(
&self,
Expand Down
270 changes: 142 additions & 128 deletions c2rust-transpile/src/translator/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3125,134 +3125,9 @@ impl<'c> Translation<'c> {
}
}

DeclRef(qual_ty, decl_id, lrvalue) => {
let decl = &self
.ast_context
.get_decl(&decl_id)
.ok_or_else(|| format_err!("Missing declref {:?}", decl_id))?
.kind;
if ctx.expanding_macro.is_some() {
// TODO Determining which declarations have been declared within the scope of the const macro expr
// vs. which are out-of-scope of the const macro is non-trivial,
// so for now, we don't allow const macros referencing any declarations.
return Err(format_translation_err!(
self.ast_context.display_loc(src_loc),
"Cannot yet refer to declarations in a const expr",
));

#[allow(unreachable_code)] // TODO temporary (see above).
if let CDeclKind::Variable {
has_static_duration: true,
..
} = decl
{
return Err(format_translation_err!(
self.ast_context.display_loc(src_loc),
"Cannot refer to static duration variable in a const expression",
));
}
}

let varname = decl.get_name().expect("expected variable name").to_owned();
let rustname = self
.renamer
.borrow_mut()
.get(&decl_id)
.ok_or_else(|| format_err!("name not declared: '{}'", varname))?;

// Import the referenced global decl into our submodule
if self.tcfg.reorganize_definitions {
self.add_import(decl_id, &rustname);
// match decl {
// CDeclKind::Variable { is_defn: false, .. } => {}
// _ => self.add_import(decl_id, &rustname),
// }
}

let mut val = mk().path_expr(vec![rustname]);
let mut set_unsafe = false;

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 {
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
.try_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(..) =
self.ast_context.resolve_type(qual_ty.ctype).kind
{
val = mk().method_call_expr(val, "as_mut_ptr", vec![]);
}

let mut val = WithStmts::new_val(val).merge_unsafe(set_unsafe);

if lrvalue.is_rvalue() {
val = self.make_cast(ctx, qual_ty, override_ty.unwrap_or(qual_ty), val)?;
}

Ok(val)
}
DeclRef(result_type_id, decl_id, lrvalue) => self
.convert_decl_ref(ctx, override_ty, result_type_id, decl_id, lrvalue)
.map_err(|e| e.add_loc(self.ast_context.display_loc(src_loc))),

OffsetOf(ty, ref kind) => match kind {
OffsetOfKind::Constant(val) => Ok(WithStmts::new_val(self.mk_int_lit(
Expand Down Expand Up @@ -3506,6 +3381,145 @@ impl<'c> Translation<'c> {
}
}

fn convert_decl_ref(
&self,
ctx: ExprContext,
expected_type_id: Option<CQualTypeId>,
result_type_id: CQualTypeId,
decl_id: CDeclId,
lrvalue: LRValue,
) -> TranslationResult<WithStmts<Box<Expr>>> {
let decl = &self
.ast_context
.get_decl(&decl_id)
.ok_or_else(|| format_err!("Missing declref {:?}", decl_id))?
.kind;
if ctx.expanding_macro.is_some() {
// TODO Determining which declarations have been declared within the scope of the const macro expr
// vs. which are out-of-scope of the const macro is non-trivial,
// so for now, we don't allow const macros referencing any declarations.
return Err(format_translation_err!(
None,
"Cannot yet refer to declarations in a const expr",
));

#[allow(unreachable_code)] // TODO temporary (see above).
if let CDeclKind::Variable {
has_static_duration: true,
..
} = decl
{
return Err(format_translation_err!(
None,
"Cannot refer to static duration variable in a const expression",
));
}
}

if let CDeclKind::EnumConstant { .. } = decl {
return self.convert_enum_constant_decl_ref(result_type_id, decl_id);
}

let varname = decl.get_name().expect("expected variable name").to_owned();
let rustname = self
.renamer
.borrow_mut()
.get(&decl_id)
.ok_or_else(|| format_err!("name not declared: '{}'", varname))?;

// Import the referenced global decl into our submodule
if self.tcfg.reorganize_definitions {
self.add_import(decl_id, &rustname);
// match decl {
// CDeclKind::Variable { is_defn: false, .. } => {}
// _ => self.add_import(decl_id, &rustname),
// }
}

let mut val = mk().path_expr(vec![rustname]);
let mut set_unsafe = false;

match decl {
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(result_type_id.ctype)?;
let actual_ty = self
.type_converter
.borrow_mut()
.knr_function_type_with_parameters(
&self.ast_context,
result_type_id.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(result_type_id.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
.try_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(..) =
self.ast_context.resolve_type(result_type_id.ctype).kind
{
val = mk().method_call_expr(val, "as_mut_ptr", vec![]);
}

let mut val = WithStmts::new_val(val).merge_unsafe(set_unsafe);

if lrvalue.is_rvalue() {
val = self.make_cast(
ctx,
result_type_id,
expected_type_id.unwrap_or(result_type_id),
val,
)?;
}

Ok(val)
}

pub fn convert_constant(&self, constant: ConstIntExpr) -> TranslationResult<Box<Expr>> {
let expr = match constant {
ConstIntExpr::U(n) => mk().lit_expr(mk().int_unsuffixed_lit(n as u128)),
Expand Down
Loading