fn try_lower_expression_unary(
expr: &Expression,
ctx: &mut LoweringContext,
instructions: &mut Vec<Instruction>,
) -> Option<bool> {
match expr {
Expression::Not(_, inner) => Some(if lower_expression(inner, ctx, instructions) {
instructions.push(Instruction::LogicalNot);
true
} else {
false
}),
Expression::BitwiseNot(_, inner) => {
let ty = infer_type_from_expression(inner, ctx);
if !lower_expression(inner, ctx, instructions) {
return Some(false);
}
match ty {
Some(ValueType::Integer { signed: false, bits: 256 }) => {
let max_u256: BigInt = (BigInt::one() << 256usize) - BigInt::one();
instructions.push(Instruction::PushLiteral(LiteralValue::Integer(max_u256)));
instructions.push(Instruction::BinaryOp(BinaryOperator::BitXor));
}
Some(ValueType::Integer { signed: false, bits }) if bits < 256 => {
instructions.push(Instruction::BitwiseNot);
emit_truncate_narrow_unsigned(instructions, bits);
}
Some(ValueType::Integer { signed: true, bits }) if bits < 256 => {
instructions.push(Instruction::BitwiseNot);
emit_truncate_narrow_signed(ctx, instructions, bits);
}
_ => {
instructions.push(Instruction::BitwiseNot);
}
}
Some(true)
}
Expression::Power(_, left, right) => Some(lower_power_expression(
left.as_ref(),
right.as_ref(),
ctx,
instructions,
)),
Expression::UnaryPlus(_, inner) => Some(lower_expression(inner, ctx, instructions)),
Expression::Negate(_, inner) => Some(lower_negate_expression(inner, ctx, instructions)),
_ => None,
}
}
fn lower_negate_expression(
inner: &Expression,
ctx: &mut LoweringContext,
instructions: &mut Vec<Instruction>,
) -> bool {
let emit_guard = should_emit_negate_guard(inner, ctx);
let intn_min = signed_intn_min_literal(inner, ctx);
if !lower_expression(inner, ctx, instructions) {
return false;
}
if emit_guard {
if let Some(min_value) = intn_min {
instructions.push(Instruction::Dup);
instructions.push(Instruction::PushLiteral(LiteralValue::Integer(min_value)));
instructions.push(Instruction::BinaryOp(BinaryOperator::Eq));
let safe_label = ctx.next_label();
instructions.push(Instruction::JumpIf { target: safe_label });
emit_panic(0x11, instructions);
instructions.push(Instruction::Label(safe_label));
}
}
instructions.push(Instruction::PushLiteral(LiteralValue::Integer(
BigInt::from(-1),
)));
instructions.push(Instruction::BinaryOp(BinaryOperator::Mul));
true
}
fn should_emit_negate_guard(inner: &Expression, ctx: &LoweringContext) -> bool {
if ctx.in_unchecked_block() {
return false;
}
if is_literal_number(inner) {
return false;
}
matches!(
infer_type_from_expression(inner, ctx),
Some(ValueType::Integer { signed: true, .. })
)
}
fn signed_intn_min_literal(inner: &Expression, ctx: &LoweringContext) -> Option<BigInt> {
if let Some(ValueType::Integer {
signed: true,
bits,
}) = infer_type_from_expression(inner, ctx)
{
let one: BigInt = BigInt::from(1);
let shifted: BigInt = one << (bits as u32 - 1);
Some(-shifted)
} else {
None
}
}