Checked arithmetic by default.

This commit is contained in:
chriseth
2020-10-19 16:58:37 +02:00
parent 41000fea31
commit 527c073bb9
21 changed files with 405 additions and 98 deletions
+5
View File
@@ -123,6 +123,9 @@ public:
void setMostDerivedContract(ContractDefinition const& _contract) { m_mostDerivedContract = &_contract; }
ContractDefinition const& mostDerivedContract() const;
void setArithmetic(Arithmetic _value) { m_arithmetic = _value; }
Arithmetic arithmetic() const { return m_arithmetic; }
/// @returns the next function in the queue of functions that are still to be compiled
/// (i.e. that were referenced during compilation but where we did not yet generate code for).
/// Returns nullptr if the queue is empty. Does not remove the function from the queue,
@@ -380,6 +383,8 @@ private:
std::map<Declaration const*, std::vector<unsigned>> m_localVariables;
/// The contract currently being compiled. Virtual function lookup starts from this contarct.
ContractDefinition const* m_mostDerivedContract = nullptr;
/// Whether to use checked arithmetic.
Arithmetic m_arithmetic = Arithmetic::Checked;
/// Stack of current visited AST nodes, used for location attachment
std::stack<ASTNode const*> m_visitedNodes;
/// The runtime context if in Creation mode, this is used for generating tags that would be stored into the storage and then used at runtime.
+14
View File
@@ -1247,19 +1247,31 @@ bool ContractCompiler::visit(PlaceholderStatement const& _placeholderStatement)
{
StackHeightChecker checker(m_context);
CompilerContext::LocationSetter locationSetter(m_context, _placeholderStatement);
solAssert(m_context.arithmetic() == Arithmetic::Checked, "Placeholder cannot be used inside checked block.");
appendModifierOrFunctionCode();
solAssert(m_context.arithmetic() == Arithmetic::Checked, "Arithmetic not reset to 'checked'.");
checker.check();
return true;
}
bool ContractCompiler::visit(Block const& _block)
{
if (_block.unchecked())
{
solAssert(m_context.arithmetic() == Arithmetic::Checked, "");
m_context.setArithmetic(Arithmetic::Wrapping);
}
storeStackHeight(&_block);
return true;
}
void ContractCompiler::endVisit(Block const& _block)
{
if (_block.unchecked())
{
solAssert(m_context.arithmetic() == Arithmetic::Wrapping, "");
m_context.setArithmetic(Arithmetic::Checked);
}
// Frees local variables declared in the scope of this block.
popScopedVariables(&_block);
}
@@ -1327,6 +1339,8 @@ void ContractCompiler::appendModifierOrFunctionCode()
if (codeBlock)
{
m_context.setArithmetic(Arithmetic::Checked);
std::set<ExperimentalFeature> experimentalFeaturesOutside = m_context.experimentalFeaturesActive();
m_context.setExperimentalFeatures(codeBlock->sourceUnit().annotation().experimentalFeatures);
+100 -39
View File
@@ -275,7 +275,7 @@ bool ExpressionCompiler::visit(Assignment const& _assignment)
solAssert(*_assignment.annotation().type == leftType, "");
bool cleanupNeeded = false;
if (op != Token::Assign)
cleanupNeeded = cleanupNeededForOp(leftType.category(), binOp);
cleanupNeeded = cleanupNeededForOp(leftType.category(), binOp, m_context.arithmetic());
_assignment.rightHandSide().accept(*this);
// Perform some conversion already. This will convert storage types to memory and literals
// to their actual type, but will not convert e.g. memory to storage.
@@ -381,9 +381,10 @@ bool ExpressionCompiler::visit(TupleExpression const& _tuple)
bool ExpressionCompiler::visit(UnaryOperation const& _unaryOperation)
{
CompilerContext::LocationSetter locationSetter(m_context, _unaryOperation);
if (_unaryOperation.annotation().type->category() == Type::Category::RationalNumber)
Type const& type = *_unaryOperation.annotation().type;
if (type.category() == Type::Category::RationalNumber)
{
m_context << _unaryOperation.annotation().type->literalValue(nullptr);
m_context << type.literalValue(nullptr);
return false;
}
@@ -406,24 +407,39 @@ bool ExpressionCompiler::visit(UnaryOperation const& _unaryOperation)
case Token::Dec: // -- (pre- or postfix)
solAssert(!!m_currentLValue, "LValue not retrieved.");
solUnimplementedAssert(
_unaryOperation.annotation().type->category() != Type::Category::FixedPoint,
type.category() != Type::Category::FixedPoint,
"Not yet implemented - FixedPointType."
);
m_currentLValue->retrieveValue(_unaryOperation.location());
if (!_unaryOperation.isPrefixOperation())
{
// store value for later
solUnimplementedAssert(_unaryOperation.annotation().type->sizeOnStack() == 1, "Stack size != 1 not implemented.");
solUnimplementedAssert(type.sizeOnStack() == 1, "Stack size != 1 not implemented.");
m_context << Instruction::DUP1;
if (m_currentLValue->sizeOnStack() > 0)
for (unsigned i = 1 + m_currentLValue->sizeOnStack(); i > 0; --i)
m_context << swapInstruction(i);
}
m_context << u256(1);
if (_unaryOperation.getOperator() == Token::Inc)
m_context << Instruction::ADD;
{
if (m_context.arithmetic() == Arithmetic::Checked)
m_context.callYulFunction(m_context.utilFunctions().incrementCheckedFunction(type), 1, 1);
else
{
m_context << u256(1);
m_context << Instruction::ADD;
}
}
else
m_context << Instruction::SWAP1 << Instruction::SUB;
{
if (m_context.arithmetic() == Arithmetic::Checked)
m_context.callYulFunction(m_context.utilFunctions().decrementCheckedFunction(type), 1, 1);
else
{
m_context << u256(1);
m_context << Instruction::SWAP1 << Instruction::SUB;
}
}
// Stack for prefix: [ref...] (*ref)+-1
// Stack for postfix: *ref [ref...] (*ref)+-1
for (unsigned i = m_currentLValue->sizeOnStack(); i > 0; --i)
@@ -437,7 +453,10 @@ bool ExpressionCompiler::visit(UnaryOperation const& _unaryOperation)
// unary add, so basically no-op
break;
case Token::Sub: // -
m_context << u256(0) << Instruction::SUB;
if (m_context.arithmetic() == Arithmetic::Checked)
m_context.callYulFunction(m_context.utilFunctions().negateNumberCheckedFunction(type), 1, 1);
else
m_context << u256(0) << Instruction::SUB;
break;
default:
solAssert(false, "Invalid unary operator: " + string(TokenTraits::toString(_unaryOperation.getOperator())));
@@ -460,7 +479,7 @@ bool ExpressionCompiler::visit(BinaryOperation const& _binaryOperation)
m_context << commonType->literalValue(nullptr);
else
{
bool cleanupNeeded = cleanupNeededForOp(commonType->category(), c_op);
bool cleanupNeeded = cleanupNeededForOp(commonType->category(), c_op, m_context.arithmetic());
TypePointer leftTargetType = commonType;
TypePointer rightTargetType =
@@ -2112,34 +2131,65 @@ void ExpressionCompiler::appendArithmeticOperatorCode(Token _operator, Type cons
solUnimplemented("Not yet implemented - FixedPointType.");
IntegerType const& type = dynamic_cast<IntegerType const&>(_type);
bool const c_isSigned = type.isSigned();
switch (_operator)
if (m_context.arithmetic() == Arithmetic::Checked)
{
case Token::Add:
m_context << Instruction::ADD;
break;
case Token::Sub:
m_context << Instruction::SUB;
break;
case Token::Mul:
m_context << Instruction::MUL;
break;
case Token::Div:
case Token::Mod:
{
// Test for division by zero
m_context << Instruction::DUP2 << Instruction::ISZERO;
m_context.appendConditionalInvalid();
if (_operator == Token::Div)
m_context << (c_isSigned ? Instruction::SDIV : Instruction::DIV);
else
m_context << (c_isSigned ? Instruction::SMOD : Instruction::MOD);
break;
string functionName;
switch (_operator)
{
case Token::Add:
functionName = m_context.utilFunctions().overflowCheckedIntAddFunction(type);
break;
case Token::Sub:
functionName = m_context.utilFunctions().overflowCheckedIntSubFunction(type);
break;
case Token::Mul:
functionName = m_context.utilFunctions().overflowCheckedIntMulFunction(type);
break;
case Token::Div:
functionName = m_context.utilFunctions().overflowCheckedIntDivFunction(type);
break;
case Token::Mod:
functionName = m_context.utilFunctions().intModFunction(type);
break;
case Token::Exp:
// EXP is handled in a different function.
default:
solAssert(false, "Unknown arithmetic operator.");
}
// TODO Maybe we want to force-inline this?
m_context.callYulFunction(functionName, 2, 1);
}
default:
solAssert(false, "Unknown arithmetic operator.");
else
{
bool const c_isSigned = type.isSigned();
switch (_operator)
{
case Token::Add:
m_context << Instruction::ADD;
break;
case Token::Sub:
m_context << Instruction::SUB;
break;
case Token::Mul:
m_context << Instruction::MUL;
break;
case Token::Div:
case Token::Mod:
{
// Test for division by zero
m_context << Instruction::DUP2 << Instruction::ISZERO;
m_context.appendConditionalInvalid();
if (_operator == Token::Div)
m_context << (c_isSigned ? Instruction::SDIV : Instruction::DIV);
else
m_context << (c_isSigned ? Instruction::SMOD : Instruction::MOD);
break;
}
default:
solAssert(false, "Unknown arithmetic operator.");
}
}
}
@@ -2237,7 +2287,14 @@ void ExpressionCompiler::appendExpOperatorCode(Type const& _valueType, Type cons
solAssert(_valueType.category() == Type::Category::Integer, "");
solAssert(!dynamic_cast<IntegerType const&>(_exponentType).isSigned(), "");
m_context << Instruction::EXP;
if (m_context.arithmetic() == Arithmetic::Checked)
m_context.callYulFunction(m_context.utilFunctions().overflowCheckedIntExpFunction(
dynamic_cast<IntegerType const&>(_valueType),
dynamic_cast<IntegerType const&>(_exponentType)
), 2, 1);
else
m_context << Instruction::EXP;
}
void ExpressionCompiler::appendExternalFunctionCall(
@@ -2561,11 +2618,15 @@ void ExpressionCompiler::setLValueToStorageItem(Expression const& _expression)
setLValue<StorageItem>(_expression, *_expression.annotation().type);
}
bool ExpressionCompiler::cleanupNeededForOp(Type::Category _type, Token _op)
bool ExpressionCompiler::cleanupNeededForOp(Type::Category _type, Token _op, Arithmetic _arithmetic)
{
if (TokenTraits::isCompareOp(_op) || TokenTraits::isShiftOp(_op))
return true;
else if (_type == Type::Category::Integer && (_op == Token::Div || _op == Token::Mod || _op == Token::Exp))
else if (
_arithmetic == Arithmetic::Wrapping &&
_type == Type::Category::Integer &&
(_op == Token::Div || _op == Token::Mod || _op == Token::Exp)
)
// We need cleanup for EXP because 0**0 == 1, but 0**0x100 == 0
// It would suffice to clean the exponent, though.
return true;
+1 -1
View File
@@ -132,7 +132,7 @@ private:
/// @returns true if the operator applied to the given type requires a cleanup prior to the
/// operation.
static bool cleanupNeededForOp(Type::Category _type, Token _op);
static bool cleanupNeededForOp(Type::Category _type, Token _op, Arithmetic _arithmetic);
void acceptAndConvert(Expression const& _expression, Type const& _type, bool _cleanupNeeded = false);
+155 -29
View File
@@ -483,6 +483,22 @@ string YulUtilFunctions::overflowCheckedIntAddFunction(IntegerType const& _type)
});
}
string YulUtilFunctions::wrappingIntAddFunction(IntegerType const& _type)
{
string functionName = "wrapping_add_" + _type.identifier();
return m_functionCollector.createFunction(functionName, [&]() {
return
Whiskers(R"(
function <functionName>(x, y) -> sum {
sum := <cleanupFunction>(add(x, y))
}
)")
("functionName", functionName)
("cleanupFunction", cleanupFunction(_type))
.render();
});
}
string YulUtilFunctions::overflowCheckedIntMulFunction(IntegerType const& _type)
{
string functionName = "checked_mul_" + _type.identifier();
@@ -519,6 +535,22 @@ string YulUtilFunctions::overflowCheckedIntMulFunction(IntegerType const& _type)
});
}
string YulUtilFunctions::wrappingIntMulFunction(IntegerType const& _type)
{
string functionName = "wrapping_mul_" + _type.identifier();
return m_functionCollector.createFunction(functionName, [&]() {
return
Whiskers(R"(
function <functionName>(x, y) -> product {
product := <cleanupFunction>(mul(x, y))
}
)")
("functionName", functionName)
("cleanupFunction", cleanupFunction(_type))
.render();
});
}
string YulUtilFunctions::overflowCheckedIntDivFunction(IntegerType const& _type)
{
string functionName = "checked_div_" + _type.identifier();
@@ -548,9 +580,30 @@ string YulUtilFunctions::overflowCheckedIntDivFunction(IntegerType const& _type)
});
}
string YulUtilFunctions::checkedIntModFunction(IntegerType const& _type)
string YulUtilFunctions::wrappingIntDivFunction(IntegerType const& _type)
{
string functionName = "checked_mod_" + _type.identifier();
string functionName = "wrapping_div_" + _type.identifier();
return m_functionCollector.createFunction(functionName, [&]() {
return
Whiskers(R"(
function <functionName>(x, y) -> r {
x := <cleanupFunction>(x)
y := <cleanupFunction>(y)
if iszero(y) { <error>() }
r := <?signed>s</signed>div(x, y)
}
)")
("functionName", functionName)
("cleanupFunction", cleanupFunction(_type))
("signed", _type.isSigned())
("error", panicFunction())
.render();
});
}
string YulUtilFunctions::intModFunction(IntegerType const& _type)
{
string functionName = "mod_" + _type.identifier();
return m_functionCollector.createFunction(functionName, [&]() {
return
Whiskers(R"(
@@ -599,6 +652,22 @@ string YulUtilFunctions::overflowCheckedIntSubFunction(IntegerType const& _type)
});
}
string YulUtilFunctions::wrappingIntSubFunction(IntegerType const& _type)
{
string functionName = "wrapping_sub_" + _type.identifier();
return m_functionCollector.createFunction(functionName, [&] {
return
Whiskers(R"(
function <functionName>(x, y) -> diff {
diff := <cleanupFunction>(sub(x, y))
}
)")
("functionName", functionName)
("cleanupFunction", cleanupFunction(_type))
.render();
});
}
string YulUtilFunctions::overflowCheckedIntExpFunction(
IntegerType const& _type,
IntegerType const& _exponentType
@@ -894,6 +963,30 @@ string YulUtilFunctions::overflowCheckedExpLoopFunction()
});
}
string YulUtilFunctions::wrappingIntExpFunction(
IntegerType const& _type,
IntegerType const& _exponentType
)
{
solAssert(!_exponentType.isSigned(), "");
string functionName = "wrapping_exp_" + _type.identifier() + "_" + _exponentType.identifier();
return m_functionCollector.createFunction(functionName, [&]() {
return
Whiskers(R"(
function <functionName>(base, exponent) -> power {
base := <baseCleanupFunction>(base)
exponent := <exponentCleanupFunction>(exponent)
power := <baseCleanupFunction>(exp(base, exponent))
}
)")
("functionName", functionName)
("baseCleanupFunction", cleanupFunction(_type))
("exponentCleanupFunction", cleanupFunction(_exponentType))
.render();
});
}
string YulUtilFunctions::extractByteArrayLengthFunction()
{
string functionName = "extract_byte_array_length";
@@ -2951,30 +3044,39 @@ std::string YulUtilFunctions::decrementCheckedFunction(Type const& _type)
string const functionName = "decrement_" + _type.identifier();
return m_functionCollector.createFunction(functionName, [&]() {
u256 minintval;
// Smallest admissible value to decrement
if (type.isSigned())
minintval = 0 - (u256(1) << (type.numBits() - 1)) + 1;
else
minintval = 1;
return Whiskers(R"(
function <functionName>(value) -> ret {
value := <cleanupFunction>(value)
if <lt>(value, <minval>) { <panic>() }
if eq(value, <minval>) { <panic>() }
ret := sub(value, 1)
}
)")
("functionName", functionName)
("panic", panicFunction())
("minval", toCompactHexWithPrefix(minintval))
("lt", type.isSigned() ? "slt" : "lt")
("minval", toCompactHexWithPrefix(type.min()))
("cleanupFunction", cleanupFunction(_type))
.render();
});
}
std::string YulUtilFunctions::decrementWrappingFunction(Type const& _type)
{
IntegerType const& type = dynamic_cast<IntegerType const&>(_type);
string const functionName = "decrement_wrapping_" + _type.identifier();
return m_functionCollector.createFunction(functionName, [&]() {
return Whiskers(R"(
function <functionName>(value) -> ret {
ret := <cleanupFunction>(sub(value, 1))
}
)")
("functionName", functionName)
("cleanupFunction", cleanupFunction(type))
.render();
});
}
std::string YulUtilFunctions::incrementCheckedFunction(Type const& _type)
{
IntegerType const& type = dynamic_cast<IntegerType const&>(_type);
@@ -2982,55 +3084,79 @@ std::string YulUtilFunctions::incrementCheckedFunction(Type const& _type)
string const functionName = "increment_" + _type.identifier();
return m_functionCollector.createFunction(functionName, [&]() {
u256 maxintval;
// Biggest admissible value to increment
if (type.isSigned())
maxintval = (u256(1) << (type.numBits() - 1)) - 2;
else
maxintval = (u256(1) << type.numBits()) - 2;
return Whiskers(R"(
function <functionName>(value) -> ret {
value := <cleanupFunction>(value)
if <gt>(value, <maxval>) { <panic>() }
if eq(value, <maxval>) { <panic>() }
ret := add(value, 1)
}
)")
("functionName", functionName)
("maxval", toCompactHexWithPrefix(maxintval))
("gt", type.isSigned() ? "sgt" : "gt")
("maxval", toCompactHexWithPrefix(type.max()))
("panic", panicFunction())
("cleanupFunction", cleanupFunction(_type))
.render();
});
}
std::string YulUtilFunctions::incrementWrappingFunction(Type const& _type)
{
IntegerType const& type = dynamic_cast<IntegerType const&>(_type);
string const functionName = "increment_wrapping_" + _type.identifier();
return m_functionCollector.createFunction(functionName, [&]() {
return Whiskers(R"(
function <functionName>(value) -> ret {
ret := <cleanupFunction>(add(value, 1))
}
)")
("functionName", functionName)
("cleanupFunction", cleanupFunction(type))
.render();
});
}
string YulUtilFunctions::negateNumberCheckedFunction(Type const& _type)
{
IntegerType const& type = dynamic_cast<IntegerType const&>(_type);
solAssert(type.isSigned(), "Expected signed type!");
string const functionName = "negate_" + _type.identifier();
u256 const minintval = 0 - (u256(1) << (type.numBits() - 1)) + 1;
return m_functionCollector.createFunction(functionName, [&]() {
return Whiskers(R"(
function <functionName>(value) -> ret {
value := <cleanupFunction>(value)
if slt(value, <minval>) { <panic>() }
if eq(value, <minval>) { <panic>() }
ret := sub(0, value)
}
)")
("functionName", functionName)
("minval", toCompactHexWithPrefix(minintval))
("minval", toCompactHexWithPrefix(type.min()))
("cleanupFunction", cleanupFunction(_type))
("panic", panicFunction())
.render();
});
}
string YulUtilFunctions::negateNumberWrappingFunction(Type const& _type)
{
IntegerType const& type = dynamic_cast<IntegerType const&>(_type);
solAssert(type.isSigned(), "Expected signed type!");
string const functionName = "negate_" + _type.identifier();
return m_functionCollector.createFunction(functionName, [&]() {
return Whiskers(R"(
function <functionName>(value) -> ret {
value := <cleanupFunction>(sub(0, value)))
}
)")
("functionName", functionName)
("cleanupFunction", cleanupFunction(type))
.render();
});
}
string YulUtilFunctions::zeroValueFunction(Type const& _type, bool _splitFunctionTypes)
{
solAssert(_type.category() != Type::Category::Mapping, "");
+19 -1
View File
@@ -106,24 +106,35 @@ public:
/// signature: (x, y) -> sum
std::string overflowCheckedIntAddFunction(IntegerType const& _type);
/// signature: (x, y) -> sum
std::string wrappingIntAddFunction(IntegerType const& _type);
/// signature: (x, y) -> product
std::string overflowCheckedIntMulFunction(IntegerType const& _type);
/// signature: (x, y) -> product
std::string wrappingIntMulFunction(IntegerType const& _type);
/// @returns name of function to perform division on integers.
/// Checks for division by zero and the special case of
/// signed division of the smallest number by -1.
std::string overflowCheckedIntDivFunction(IntegerType const& _type);
/// @returns name of function to perform division on integers.
/// Checks for division by zero.
std::string wrappingIntDivFunction(IntegerType const& _type);
/// @returns name of function to perform modulo on integers.
/// Reverts for modulo by zero.
std::string checkedIntModFunction(IntegerType const& _type);
std::string intModFunction(IntegerType const& _type);
/// @returns computes the difference between two values.
/// Assumes the input to be in range for the type.
/// signature: (x, y) -> diff
std::string overflowCheckedIntSubFunction(IntegerType const& _type);
/// @returns computes the difference between two values.
/// signature: (x, y) -> diff
std::string wrappingIntSubFunction(IntegerType const& _type);
/// @returns the name of the exponentiation function.
/// signature: (base, exponent) -> power
std::string overflowCheckedIntExpFunction(IntegerType const& _type, IntegerType const& _exponentType);
@@ -151,6 +162,10 @@ public:
/// signature: (power, base, exponent, max) -> power
std::string overflowCheckedExpLoopFunction();
/// @returns the name of the exponentiation function.
/// signature: (base, exponent) -> power
std::string wrappingIntExpFunction(IntegerType const& _type, IntegerType const& _exponentType);
/// @returns the name of a function that fetches the length of the given
/// array
/// signature: (array) -> length
@@ -367,9 +382,12 @@ public:
std::string forwardingRevertFunction();
std::string incrementCheckedFunction(Type const& _type);
std::string incrementWrappingFunction(Type const& _type);
std::string decrementCheckedFunction(Type const& _type);
std::string decrementWrappingFunction(Type const& _type);
std::string negateNumberCheckedFunction(Type const& _type);
std::string negateNumberWrappingFunction(Type const& _type);
/// @returns the name of a function that returns the zero value for the
/// provided type.
@@ -132,6 +132,9 @@ public:
langutil::EVMVersion evmVersion() const { return m_evmVersion; };
void setArithmetic(Arithmetic _value) { m_arithmetic = _value; }
Arithmetic arithmetic() const { return m_arithmetic; }
ABIFunctions abiFunctions();
/// @returns code that stores @param _message for revert reason
@@ -161,6 +164,8 @@ private:
std::map<VariableDeclaration const*, std::pair<u256, unsigned>> m_stateVariables;
MultiUseYulFunctionCollector m_functions;
size_t m_varCounter = 0;
/// Whether to use checked or wrapping arithmetic.
Arithmetic m_arithmetic = Arithmetic::Checked;
/// Flag indicating whether any inline assembly block was seen.
bool m_inlineAssemblySeen = false;
@@ -474,6 +474,25 @@ bool IRGeneratorForStatements::visit(TupleExpression const& _tuple)
return false;
}
bool IRGeneratorForStatements::visit(Block const& _block)
{
if (_block.unchecked())
{
solAssert(m_context.arithmetic() == Arithmetic::Checked, "");
m_context.setArithmetic(Arithmetic::Wrapping);
}
return true;
}
void IRGeneratorForStatements::endVisit(Block const& _block)
{
if (_block.unchecked())
{
solAssert(m_context.arithmetic() == Arithmetic::Wrapping, "");
m_context.setArithmetic(Arithmetic::Checked);
}
}
bool IRGeneratorForStatements::visit(IfStatement const& _ifStatement)
{
_ifStatement.condition().accept(*this);
@@ -618,11 +637,11 @@ void IRGeneratorForStatements::endVisit(UnaryOperation const& _unaryOperation)
else if (op == Token::Sub)
{
IntegerType const& intType = *dynamic_cast<IntegerType const*>(&resultType);
define(_unaryOperation) <<
m_utils.negateNumberCheckedFunction(intType) <<
"(" <<
IRVariable(_unaryOperation.subExpression()).name() <<
")\n";
define(_unaryOperation) << (
m_context.arithmetic() == Arithmetic::Checked ?
m_utils.negateNumberCheckedFunction(intType) :
m_utils.negateNumberWrappingFunction(intType)
) << "(" << IRVariable(_unaryOperation.subExpression()).name() << ")\n";
}
else
solUnimplementedAssert(false, "Unary operator not yet implemented");
@@ -2560,23 +2579,23 @@ string IRGeneratorForStatements::binaryOperation(
if (IntegerType const* type = dynamic_cast<IntegerType const*>(&_type))
{
string fun;
// TODO: Implement all operations for signed and unsigned types.
bool checked = m_context.arithmetic() == Arithmetic::Checked;
switch (_operator)
{
case Token::Add:
fun = m_utils.overflowCheckedIntAddFunction(*type);
fun = checked ? m_utils.overflowCheckedIntAddFunction(*type) : m_utils.wrappingIntAddFunction(*type);
break;
case Token::Sub:
fun = m_utils.overflowCheckedIntSubFunction(*type);
fun = checked ? m_utils.overflowCheckedIntSubFunction(*type) : m_utils.wrappingIntSubFunction(*type);
break;
case Token::Mul:
fun = m_utils.overflowCheckedIntMulFunction(*type);
fun = checked ? m_utils.overflowCheckedIntMulFunction(*type) : m_utils.wrappingIntMulFunction(*type);
break;
case Token::Div:
fun = m_utils.overflowCheckedIntDivFunction(*type);
fun = checked ? m_utils.overflowCheckedIntDivFunction(*type) : m_utils.wrappingIntDivFunction(*type);
break;
case Token::Mod:
fun = m_utils.checkedIntModFunction(*type);
fun = m_utils.intModFunction(*type);
break;
case Token::BitOr:
fun = "or";
@@ -66,6 +66,8 @@ public:
bool visit(Conditional const& _conditional) override;
bool visit(Assignment const& _assignment) override;
bool visit(TupleExpression const& _tuple) override;
bool visit(Block const& _block) override;
void endVisit(Block const& _block) override;
bool visit(IfStatement const& _ifStatement) override;
bool visit(ForStatement const& _forStatement) override;
bool visit(WhileStatement const& _whileStatement) override;