Skip to content
Merged
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
12 changes: 12 additions & 0 deletions c2rust-transpile/src/translator/enums.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,18 @@ 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, 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
271 changes: 143 additions & 128 deletions c2rust-transpile/src/translator/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3276,134 +3276,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.
return self.convert_cast_from_enum(qual_ty, 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 @@ -3682,6 +3557,146 @@ impl<'c> Translation<'c> {
self.convert_cast(ctx, None, target_type_id, expr_id, kind, None, false)
}

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 {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're moving this before the reorganize_definitions check below, is this correct?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In principle yes, because convert_enum_constant_decl_ref calls enum_constant_expr, which does the importing itself. What puzzles me more is why the import is gated behind reorganize_definitions in the first place? Should enum_constant_expr be doing the same? I don't understand the original logic behind it.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In reorganize_definitions mode we emit the code coming in from #included headers in separate sub-modules. In that case, each use of a header declaration needs to import it from its sub-module, so the transpiler calls add_import. There is one interesting thing going on there: that method calls add_use_item which checks if the imported item lives in the same module as the use, and it skips the import in that case. I think that makes the if self.tcfg.reorganize_definitions check redundant before self.add_import.

It's weird to me because both checks were introduced in the same commit 7154048c138, but they still feel redundant.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added a new commit to remove the check.

return self.convert_enum_constant_decl_ref(
expected_type_id.unwrap_or(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
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