diff --git a/cpp2rust/converter/converter.cpp b/cpp2rust/converter/converter.cpp index ec3fca19..6500fe92 100644 --- a/cpp2rust/converter/converter.cpp +++ b/cpp2rust/converter/converter.cpp @@ -1869,6 +1869,8 @@ bool Converter::VisitExplicitCastExpr(clang::ExplicitCastExpr *expr) { auto type = expr->getTypeAsWritten(); auto *sub_expr = expr->getSubExpr(); if (type->isVoidType()) { + PushExprKind push(*this, ExprKind::Void); + Convert(expr->getSubExpr()); return false; } switch (expr->getStmtClass()) { @@ -1950,7 +1952,10 @@ bool Converter::VisitBinaryOperator(clang::BinaryOperator *expr) { ConvertCast(lhs_type); } } else if (expr->isCommaOp()) { - Convert(lhs); + { + PushExprKind push(*this, ExprKind::Void); + Convert(lhs); + } StrCat(token::kSemiColon); Convert(rhs); } else if (IsUnsignedArithOp(expr)) { @@ -2248,11 +2253,20 @@ bool Converter::VisitDeclRefExpr(clang::DeclRefExpr *expr) { } bool Converter::VisitParenExpr(clang::ParenExpr *expr) { + // Comma operator becomes (A, B, C) -> { A; B; C } + if (auto *bin = clang::dyn_cast(expr->getSubExpr())) { + if (bin->isCommaOp()) { + PushBrace push(*this); + Convert(expr->getSubExpr()); + return false; + } + } + // Add cast to avoid ambigous integers. Don't add cast if sub expression is a // pointer dereference because we might want to mutate the dereferenced value. bool should_add_integral_cast = expr->getType()->isIntegralOrEnumerationType() && !isAddrOf() && - !clang::isa(expr->getSubExpr()); + !isVoid() && !clang::isa(expr->getSubExpr()); PushParen outer(*this, should_add_integral_cast); { @@ -3399,7 +3413,7 @@ void Converter::ConvertUnsignedArithBinaryOperator(clang::BinaryOperator *op, void Converter::ConvertAddrOf(clang::Expr *expr, clang::QualType pointer_type) { assert(pointer_type->isPointerType()); - if (IsReferenceType(expr)) { + if (IsReferenceType(expr) || pointer_type->isFunctionPointerType()) { PushExprKind push(*this, ExprKind::AddrOf); Convert(expr); } else { diff --git a/cpp2rust/converter/converter_lib.cpp b/cpp2rust/converter/converter_lib.cpp index 6f6a537b..9ad604d7 100644 --- a/cpp2rust/converter/converter_lib.cpp +++ b/cpp2rust/converter/converter_lib.cpp @@ -814,4 +814,9 @@ ConstCastType GetConstCastType(clang::QualType to, clang::QualType from) { } } +bool TypeIsCopyable(clang::QualType ty) { + return ty->isIntegerType() || ty->isFunctionPointerType() || + ty->isFunctionType(); +} + } // namespace cpp2rust diff --git a/cpp2rust/converter/converter_lib.h b/cpp2rust/converter/converter_lib.h index 2522980d..3a3cf25d 100644 --- a/cpp2rust/converter/converter_lib.h +++ b/cpp2rust/converter/converter_lib.h @@ -180,4 +180,6 @@ enum class ConstCastType { ConstCastType GetConstCastType(clang::QualType to, clang::QualType from); +bool TypeIsCopyable(clang::QualType ty); + } // namespace cpp2rust diff --git a/cpp2rust/converter/models/converter_refcount.cpp b/cpp2rust/converter/models/converter_refcount.cpp index 11e275ed..d8a3c719 100644 --- a/cpp2rust/converter/models/converter_refcount.cpp +++ b/cpp2rust/converter/models/converter_refcount.cpp @@ -1128,6 +1128,11 @@ bool ConverterRefCount::VisitFunctionPointerCast( bool ConverterRefCount::VisitExplicitCastExpr(clang::ExplicitCastExpr *expr) { if (expr->getTypeAsWritten()->isVoidType()) { + PushExprKind push(*this, ExprKind::Void); + Convert(expr->getSubExpr()); + if (!TypeIsCopyable(expr->getSubExpr()->getType())) { + StrCat(".clone()"); + } return false; } switch (expr->getStmtClass()) { diff --git a/tests/unit/comma_operator.cpp b/tests/unit/comma_operator.cpp new file mode 100644 index 00000000..1c20fee7 --- /dev/null +++ b/tests/unit/comma_operator.cpp @@ -0,0 +1,24 @@ +#include + +int main() { + int x = 1; + int y = (x = 2, x + 1); + assert(x == 2); + assert(y == 3); + + int z = (1, 2, 3); + assert(z == 3); + + int counter = 0; + int w = (counter++, counter++, counter); + assert(counter == 2); + assert(w == 2); + + int a = 0, b = 0; + if ((a = 1, b = 2, a + b > 0)) { + assert(a == 1); + assert(b == 2); + } + + return 0; +} diff --git a/tests/unit/out/refcount/bool_printing.rs b/tests/unit/out/refcount/bool_printing.rs index dcf2cf70..757575d4 100644 --- a/tests/unit/out/refcount/bool_printing.rs +++ b/tests/unit/out/refcount/bool_printing.rs @@ -23,12 +23,12 @@ fn main_0() -> i32 { write!( libcc2rs::cout(), "{:}\n", - ((((*i1.borrow()) != (*i2.borrow())) as bool) as u8), + (((*i1.borrow()) != (*i2.borrow())) as u8), ); write!( libcc2rs::cout(), "{:}\n", - ((((*i1.borrow()) == (*i2.borrow())) as bool) as u8), + (((*i1.borrow()) == (*i2.borrow())) as u8), ); write!(libcc2rs::cout(), "{:}\n", (({ foo_0() }) as u8),); write!(libcc2rs::cout(), "{:}\n", (({ bar_1() }) as u8),); diff --git a/tests/unit/out/refcount/comma_operator.rs b/tests/unit/out/refcount/comma_operator.rs new file mode 100644 index 00000000..5d643a5c --- /dev/null +++ b/tests/unit/out/refcount/comma_operator.rs @@ -0,0 +1,45 @@ +extern crate libcc2rs; +use libcc2rs::*; +use std::cell::RefCell; +use std::collections::BTreeMap; +use std::io::prelude::*; +use std::io::{Read, Seek, Write}; +use std::os::fd::AsFd; +use std::rc::{Rc, Weak}; +pub fn main() { + std::process::exit(main_0()); +} +fn main_0() -> i32 { + let x: Value = Rc::new(RefCell::new(1)); + let y: Value = Rc::new(RefCell::new({ + (*x.borrow_mut()) = 2; + ((*x.borrow()) + 1) + })); + assert!(((*x.borrow()) == 2)); + assert!(((*y.borrow()) == 3)); + let z: Value = Rc::new(RefCell::new({ + 1; + 2; + 3 + })); + assert!(((*z.borrow()) == 3)); + let counter: Value = Rc::new(RefCell::new(0)); + let w: Value = Rc::new(RefCell::new({ + (*counter.borrow_mut()).postfix_inc(); + (*counter.borrow_mut()).postfix_inc(); + (*counter.borrow()) + })); + assert!(((*counter.borrow()) == 2)); + assert!(((*w.borrow()) == 2)); + let a: Value = Rc::new(RefCell::new(0)); + let b: Value = Rc::new(RefCell::new(0)); + if { + (*a.borrow_mut()) = 1; + (*b.borrow_mut()) = 2; + (((*a.borrow()) + (*b.borrow())) > 0) + } { + assert!(((*a.borrow()) == 1)); + assert!(((*b.borrow()) == 2)); + } + return 0; +} diff --git a/tests/unit/out/refcount/continue.rs b/tests/unit/out/refcount/continue.rs index 593780e3..50c33632 100644 --- a/tests/unit/out/refcount/continue.rs +++ b/tests/unit/out/refcount/continue.rs @@ -35,14 +35,14 @@ fn main_0() -> i32 { 'loop_: while ((*k2.borrow()) < 5) { let k3: Value = Rc::new(RefCell::new(0)); 'loop_: while ((*k3.borrow()) < 5) { - if ((((((*k1.borrow()) + (*k2.borrow())) + (*k3.borrow())) as i32) % 2) == 0) { + if (((((*k1.borrow()) + (*k2.borrow())) + (*k3.borrow())) % 2) == 0) { (*k3.borrow_mut()).postfix_inc(); continue 'loop_; } (*out.borrow_mut()).prefix_inc(); (*k3.borrow_mut()).postfix_inc(); } - if (((((*k1.borrow()) + (*k2.borrow())) as i32) % 2) == 0) { + if ((((*k1.borrow()) + (*k2.borrow())) % 2) == 0) { (*k2.borrow_mut()).postfix_inc(); continue 'loop_; } diff --git a/tests/unit/out/refcount/reinterpret_cast_oob_read.rs b/tests/unit/out/refcount/reinterpret_cast_oob_read.rs index de214de2..e80e9c85 100644 --- a/tests/unit/out/refcount/reinterpret_cast_oob_read.rs +++ b/tests/unit/out/refcount/reinterpret_cast_oob_read.rs @@ -15,5 +15,6 @@ fn main_0() -> i32 { let x: Value = Rc::new(RefCell::new( ((*bytes.borrow()).offset((4) as isize).read()), )); + (*x.borrow_mut()); return 0; } diff --git a/tests/unit/out/refcount/reinterpret_cast_undersize.rs b/tests/unit/out/refcount/reinterpret_cast_undersize.rs index 932d7bb3..5191b981 100644 --- a/tests/unit/out/refcount/reinterpret_cast_undersize.rs +++ b/tests/unit/out/refcount/reinterpret_cast_undersize.rs @@ -13,5 +13,6 @@ fn main_0() -> i32 { let b: Value = Rc::new(RefCell::new(66_u8)); let p: Value> = Rc::new(RefCell::new((b.as_pointer()).reinterpret_cast::())); let val: Value = Rc::new(RefCell::new(((*p.borrow()).read()))); + (*val.borrow_mut()); return 0; } diff --git a/tests/unit/out/refcount/string_escape.rs b/tests/unit/out/refcount/string_escape.rs index 5909c617..85541a27 100644 --- a/tests/unit/out/refcount/string_escape.rs +++ b/tests/unit/out/refcount/string_escape.rs @@ -23,8 +23,8 @@ fn main_0() -> i32 { ); let i: Value = Rc::new(RefCell::new(0)); 'loop_: while ((*i.borrow()) - < ((((::std::mem::size_of::<[u8; 40]>() as u64 as u64) - .wrapping_div(::std::mem::size_of::() as u64 as u64)) as u64) as i32)) + < (((::std::mem::size_of::<[u8; 40]>() as u64 as u64) + .wrapping_div(::std::mem::size_of::() as u64 as u64)) as i32)) { assert!({ let _lhs = (((*special.borrow()).offset((*i.borrow()) as isize).read()) as i32); diff --git a/tests/unit/out/refcount/va_arg_printf.rs b/tests/unit/out/refcount/va_arg_printf.rs index 181f8423..b351e887 100644 --- a/tests/unit/out/refcount/va_arg_printf.rs +++ b/tests/unit/out/refcount/va_arg_printf.rs @@ -9,6 +9,7 @@ use std::rc::{Rc, Weak}; pub fn logf_impl_0(fmt: Ptr, ap: VaList) -> i32 { let fmt: Value> = Rc::new(RefCell::new(fmt)); let ap: Value = Rc::new(RefCell::new(ap)); + (*fmt.borrow()).clone(); return { let _lhs = ((*ap.borrow_mut()).arg::()).clone(); _lhs + ((*ap.borrow_mut()).arg::()).clone() diff --git a/tests/unit/out/refcount/void_cast.rs b/tests/unit/out/refcount/void_cast.rs new file mode 100644 index 00000000..e372fef3 --- /dev/null +++ b/tests/unit/out/refcount/void_cast.rs @@ -0,0 +1,97 @@ +extern crate libcc2rs; +use libcc2rs::*; +use std::cell::RefCell; +use std::collections::BTreeMap; +use std::io::prelude::*; +use std::io::{Read, Seek, Write}; +use std::os::fd::AsFd; +use std::rc::{Rc, Weak}; +pub fn unused_param_0(x: i32) { + let x: Value = Rc::new(RefCell::new(x)); + (*x.borrow_mut()); +} +thread_local!( + pub static side_effect_counter: Value = Rc::new(RefCell::new(0)); +); +pub fn bump_and_return_1() -> i32 { + (*side_effect_counter.with(Value::clone).borrow_mut()).prefix_inc(); + return (*side_effect_counter.with(Value::clone).borrow()); +} +#[derive(Default)] +pub struct Holder { + pub field: Value, +} +impl Clone for Holder { + fn clone(&self) -> Self { + let mut this = Self { + field: Rc::new(RefCell::new((*self.field.borrow()))), + }; + this + } +} +impl ByteRepr for Holder {} +pub fn main() { + std::process::exit(main_0()); +} +fn main_0() -> i32 { + ({ + let _x: i32 = 42; + unused_param_0(_x) + }); + let y: Value = Rc::new(RefCell::new(5)); + (*y.borrow_mut()); + let z: Value = Rc::new(RefCell::new({ + (*y.borrow_mut()); + 7 + })); + assert!(((*z.borrow()) == 7)); + let counter: Value = Rc::new(RefCell::new(0)); + let w: Value = Rc::new(RefCell::new({ + (*counter.borrow_mut()); + (*counter.borrow_mut()) = 3; + (*counter.borrow()) + })); + assert!(((*w.borrow()) == 3)); + assert!(((*counter.borrow()) == 3)); + ({ bump_and_return_1() }); + assert!(((*side_effect_counter.with(Value::clone).borrow()) == 1)); + let v: Value = Rc::new(RefCell::new({ + ({ bump_and_return_1() }); + 99 + })); + assert!(((*side_effect_counter.with(Value::clone).borrow()) == 2)); + assert!(((*v.borrow()) == 99)); + 0; + (0); + (*y.borrow_mut()); + (0); + (*y.borrow_mut()); + let err: Value = Rc::new(RefCell::new(0)); + ((*err.borrow_mut()) = 42); + assert!(((*err.borrow()) == 42)); + let chosen: Value = Rc::new(RefCell::new({ + ((*err.borrow_mut()) = 7); + 123 + })); + assert!(((*err.borrow()) == 7)); + assert!(((*chosen.borrow()) == 123)); + bump_and_return_1; + assert!(((*side_effect_counter.with(Value::clone).borrow()) == 2)); + (FnPtr:: i32>::new(bump_and_return_1)); + assert!(((*side_effect_counter.with(Value::clone).borrow()) == 2)); + ((FnPtr:: i32>::new(bump_and_return_1)).cast:: i32>(None)); + assert!(((*side_effect_counter.with(Value::clone).borrow()) == 2)); + let storage: Value = Rc::new(RefCell::new(11)); + let p: Value> = Rc::new(RefCell::new((storage.as_pointer()))); + ((*p.borrow()).read()); + (*p.borrow_mut()).clone(); + let arr: Value> = Rc::new(RefCell::new(Box::new([1, 2, 3]))); + ((*arr.borrow_mut())[(1) as usize]); + let h: Value = Rc::new(RefCell::new(Holder { + field: Rc::new(RefCell::new(17)), + })); + (*(*h.borrow()).field.borrow_mut()); + let hp: Value> = Rc::new(RefCell::new((h.as_pointer()))); + (*(*(*hp.borrow()).upgrade().deref()).field.borrow_mut()); + return 0; +} diff --git a/tests/unit/out/unsafe/bool_printing.rs b/tests/unit/out/unsafe/bool_printing.rs index ec11b18a..0716902d 100644 --- a/tests/unit/out/unsafe/bool_printing.rs +++ b/tests/unit/out/unsafe/bool_printing.rs @@ -51,7 +51,7 @@ unsafe fn main_0() -> i32 { .into_raw_fd(), ), "{:}\n", - ((((i1) != (i2)) as bool) as u8), + (((i1) != (i2)) as u8), ); write!( std::fs::File::from_raw_fd( @@ -62,7 +62,7 @@ unsafe fn main_0() -> i32 { .into_raw_fd(), ), "{:}\n", - ((((i1) == (i2)) as bool) as u8), + (((i1) == (i2)) as u8), ); write!( std::fs::File::from_raw_fd( diff --git a/tests/unit/out/unsafe/comma_operator.rs b/tests/unit/out/unsafe/comma_operator.rs new file mode 100644 index 00000000..7defe1c3 --- /dev/null +++ b/tests/unit/out/unsafe/comma_operator.rs @@ -0,0 +1,47 @@ +extern crate libc; +use libc::*; +extern crate libcc2rs; +use libcc2rs::*; +use std::collections::BTreeMap; +use std::io::{Read, Seek, Write}; +use std::os::fd::{AsFd, FromRawFd, IntoRawFd}; +use std::rc::Rc; +pub fn main() { + unsafe { + std::process::exit(main_0() as i32); + } +} +unsafe fn main_0() -> i32 { + let mut x: i32 = 1; + let mut y: i32 = { + x = 2; + ((x) + (1)) + }; + assert!(((x) == (2))); + assert!(((y) == (3))); + let mut z: i32 = { + 1; + 2; + 3 + }; + assert!(((z) == (3))); + let mut counter: i32 = 0; + let mut w: i32 = { + counter.postfix_inc(); + counter.postfix_inc(); + counter + }; + assert!(((counter) == (2))); + assert!(((w) == (2))); + let mut a: i32 = 0; + let mut b: i32 = 0; + if { + a = 1; + b = 2; + (((a) + (b)) > (0)) + } { + assert!(((a) == (1))); + assert!(((b) == (2))); + } + return 0; +} diff --git a/tests/unit/out/unsafe/continue.rs b/tests/unit/out/unsafe/continue.rs index ad204b1e..a26dd848 100644 --- a/tests/unit/out/unsafe/continue.rs +++ b/tests/unit/out/unsafe/continue.rs @@ -37,14 +37,14 @@ unsafe fn main_0() -> i32 { 'loop_: while ((k2) < (5)) { let mut k3: i32 = 0; 'loop_: while ((k3) < (5)) { - if ((((((k1) + (k2)) + (k3)) as i32) % (2)) == (0)) { + if (((((k1) + (k2)) + (k3)) % (2)) == (0)) { k3.postfix_inc(); continue 'loop_; } out.prefix_inc(); k3.postfix_inc(); } - if (((((k1) + (k2)) as i32) % (2)) == (0)) { + if ((((k1) + (k2)) % (2)) == (0)) { k2.postfix_inc(); continue 'loop_; } diff --git a/tests/unit/out/unsafe/reinterpret_cast_oob_read.rs b/tests/unit/out/unsafe/reinterpret_cast_oob_read.rs index 3b211f4d..754cca82 100644 --- a/tests/unit/out/unsafe/reinterpret_cast_oob_read.rs +++ b/tests/unit/out/unsafe/reinterpret_cast_oob_read.rs @@ -15,5 +15,6 @@ unsafe fn main_0() -> i32 { let mut val: u32 = 67305985_u32; let mut bytes: *mut u8 = ((&mut val as *mut u32) as *mut u8); let mut x: u8 = (*bytes.offset((4) as isize)); + x; return 0; } diff --git a/tests/unit/out/unsafe/reinterpret_cast_undersize.rs b/tests/unit/out/unsafe/reinterpret_cast_undersize.rs index 70f50953..7bbed02e 100644 --- a/tests/unit/out/unsafe/reinterpret_cast_undersize.rs +++ b/tests/unit/out/unsafe/reinterpret_cast_undersize.rs @@ -15,5 +15,6 @@ unsafe fn main_0() -> i32 { let mut b: u8 = 66_u8; let mut p: *mut u32 = ((&mut b as *mut u8) as *mut u32); let mut val: u32 = (*p); + val; return 0; } diff --git a/tests/unit/out/unsafe/string_escape.rs b/tests/unit/out/unsafe/string_escape.rs index ff21009c..285f6dde 100644 --- a/tests/unit/out/unsafe/string_escape.rs +++ b/tests/unit/out/unsafe/string_escape.rs @@ -22,8 +22,8 @@ unsafe fn main_0() -> i32 { ];; let mut i: i32 = 0; 'loop_: while ((i) - < ((((::std::mem::size_of::<[u8; 40]>() as u64 as u64) - .wrapping_div(::std::mem::size_of::() as u64 as u64)) as u64) as i32)) + < (((::std::mem::size_of::<[u8; 40]>() as u64 as u64) + .wrapping_div(::std::mem::size_of::() as u64 as u64)) as i32)) { assert!((((*special.offset((i) as isize)) as i32) == (expected[(i) as usize] as i32))); i.postfix_inc(); diff --git a/tests/unit/out/unsafe/va_arg_printf.rs b/tests/unit/out/unsafe/va_arg_printf.rs index 364d44de..6140a49c 100644 --- a/tests/unit/out/unsafe/va_arg_printf.rs +++ b/tests/unit/out/unsafe/va_arg_printf.rs @@ -7,6 +7,7 @@ use std::io::{Read, Seek, Write}; use std::os::fd::{AsFd, FromRawFd, IntoRawFd}; use std::rc::Rc; pub unsafe fn logf_impl_0(mut fmt: *const u8, mut ap: VaList) -> i32 { + fmt; return ((ap.arg::()) + (ap.arg::())); } pub unsafe fn logf_1(mut fmt: *const u8, args: &[VaArg]) -> i32 { diff --git a/tests/unit/out/unsafe/void_cast.rs b/tests/unit/out/unsafe/void_cast.rs new file mode 100644 index 00000000..af4df89d --- /dev/null +++ b/tests/unit/out/unsafe/void_cast.rs @@ -0,0 +1,88 @@ +extern crate libc; +use libc::*; +extern crate libcc2rs; +use libcc2rs::*; +use std::collections::BTreeMap; +use std::io::{Read, Seek, Write}; +use std::os::fd::{AsFd, FromRawFd, IntoRawFd}; +use std::rc::Rc; +pub unsafe fn unused_param_0(mut x: i32) { + x; +} +pub static mut side_effect_counter: i32 = 0; +pub unsafe fn bump_and_return_1() -> i32 { + side_effect_counter.prefix_inc(); + return side_effect_counter; +} +#[repr(C)] +#[derive(Copy, Clone, Default)] +pub struct Holder { + pub field: i32, +} +pub fn main() { + unsafe { + std::process::exit(main_0() as i32); + } +} +unsafe fn main_0() -> i32 { + (unsafe { + let _x: i32 = 42; + unused_param_0(_x) + }); + let mut y: i32 = 5; + y; + let mut z: i32 = { + y; + 7 + }; + assert!(((z) == (7))); + let mut counter: i32 = 0; + let mut w: i32 = { + counter; + counter = 3; + counter + }; + assert!(((w) == (3))); + assert!(((counter) == (3))); + (unsafe { bump_and_return_1() }); + assert!(((side_effect_counter) == (1))); + let mut v: i32 = { + (unsafe { bump_and_return_1() }); + 99 + }; + assert!(((side_effect_counter) == (2))); + assert!(((v) == (99))); + 0; + (0); + (y); + (0); + (y); + let mut err: i32 = 0; + (err = 42); + assert!(((err) == (42))); + let mut chosen: i32 = { + (err = 7); + 123 + }; + assert!(((err) == (7))); + assert!(((chosen) == (123))); + bump_and_return_1; + assert!(((side_effect_counter) == (2))); + (Some(bump_and_return_1)); + assert!(((side_effect_counter) == (2))); + (std::mem::transmute:: i32>, Option i32>>( + (Some(bump_and_return_1)), + )); + assert!(((side_effect_counter) == (2))); + let mut storage: i32 = 11; + let mut p: *mut i32 = (&mut storage as *mut i32); + (*p); + (p); + let mut arr: [i32; 3] = [1, 2, 3]; + (arr[(1) as usize]); + let mut h: Holder = Holder { field: 17 }; + (h.field); + let mut hp: *mut Holder = (&mut h as *mut Holder); + ((*hp).field); + return 0; +} diff --git a/tests/unit/void_cast.cpp b/tests/unit/void_cast.cpp new file mode 100644 index 00000000..78796979 --- /dev/null +++ b/tests/unit/void_cast.cpp @@ -0,0 +1,75 @@ +#include + +void unused_param(int x) { (void)x; } + +int side_effect_counter = 0; +int bump_and_return() { + ++side_effect_counter; + return side_effect_counter; +} + +struct Holder { + int field; +}; + +int main() { + unused_param(42); + + int y = 5; + (void)y; + + int z = ((void)y, 7); + assert(z == 7); + + int counter = 0; + int w = ((void)counter, counter = 3, counter); + assert(w == 3); + assert(counter == 3); + + (void)bump_and_return(); + assert(side_effect_counter == 1); + + int v = ((void)bump_and_return(), 99); + assert(side_effect_counter == 2); + assert(v == 99); + + (void)0; + (void)(0); + + (void)(y); + + ((void)0); + ((void)(y)); + + int err = 0; + ((void)(err = 42)); + assert(err == 42); + + int chosen = ((void)(err = 7), 123); + assert(err == 7); + assert(chosen == 123); + + (void)bump_and_return; + assert(side_effect_counter == 2); + + (void)(&bump_and_return); + assert(side_effect_counter == 2); + + (void)(static_cast(&bump_and_return)); + assert(side_effect_counter == 2); + + int storage = 11; + int *p = &storage; + (void)(*p); + (void)(p); + + int arr[] = {1, 2, 3}; + (void)(arr[1]); + + Holder h{17}; + (void)(h.field); + Holder *hp = &h; + (void)(hp->field); + + return 0; +}