Merge pull request #12019 from ethereum/default-messages-in-assert-macros

Default messages in assertion macros
This commit is contained in:
chriseth 2021-10-04 12:55:08 +02:00 committed by GitHub
commit a9b99dd63b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 170 additions and 56 deletions

View File

@ -28,6 +28,10 @@
#include <libsolutil/CommonData.h> #include <libsolutil/CommonData.h>
#include <liblangutil/SourceLocation.h> #include <liblangutil/SourceLocation.h>
#include <boost/preprocessor/cat.hpp>
#include <boost/preprocessor/facilities/empty.hpp>
#include <boost/preprocessor/facilities/overload.hpp>
#include <string> #include <string>
#include <utility> #include <utility>
#include <vector> #include <vector>
@ -45,18 +49,68 @@ struct FatalError: virtual util::Exception {};
struct UnimplementedFeatureError: virtual util::Exception {}; struct UnimplementedFeatureError: virtual util::Exception {};
struct InvalidAstError: virtual util::Exception {}; struct InvalidAstError: virtual util::Exception {};
/// Assertion that throws an InternalCompilerError containing the given description if it is not met. /// Assertion that throws an InternalCompilerError containing the given description if it is not met.
#define solAssert(CONDITION, DESCRIPTION) \ #if !BOOST_PP_VARIADICS_MSVC
assertThrow(CONDITION, ::solidity::langutil::InternalCompilerError, DESCRIPTION) #define solAssert(...) BOOST_PP_OVERLOAD(solAssert_,__VA_ARGS__)(__VA_ARGS__)
#else
#define solAssert(...) BOOST_PP_CAT(BOOST_PP_OVERLOAD(solAssert_,__VA_ARGS__)(__VA_ARGS__),BOOST_PP_EMPTY())
#endif
#define solUnimplementedAssert(CONDITION, DESCRIPTION) \ #define solAssert_1(CONDITION) \
assertThrow(CONDITION, ::solidity::langutil::UnimplementedFeatureError, DESCRIPTION) solAssert_2(CONDITION, "")
#define solAssert_2(CONDITION, DESCRIPTION) \
assertThrowWithDefaultDescription( \
CONDITION, \
::solidity::langutil::InternalCompilerError, \
DESCRIPTION, \
"Solidity assertion failed" \
)
/// Assertion that throws an UnimplementedFeatureError containing the given description if it is not met.
#if !BOOST_PP_VARIADICS_MSVC
#define solUnimplementedAssert(...) BOOST_PP_OVERLOAD(solUnimplementedAssert_,__VA_ARGS__)(__VA_ARGS__)
#else
#define solUnimplementedAssert(...) BOOST_PP_CAT(BOOST_PP_OVERLOAD(solUnimplementedAssert_,__VA_ARGS__)(__VA_ARGS__),BOOST_PP_EMPTY())
#endif
#define solUnimplementedAssert_1(CONDITION) \
solUnimplementedAssert_2(CONDITION, "")
#define solUnimplementedAssert_2(CONDITION, DESCRIPTION) \
assertThrowWithDefaultDescription( \
CONDITION, \
::solidity::langutil::UnimplementedFeatureError, \
DESCRIPTION, \
"Unimplemented feature" \
)
/// Helper that unconditionally reports an unimplemented feature.
#define solUnimplemented(DESCRIPTION) \ #define solUnimplemented(DESCRIPTION) \
solUnimplementedAssert(false, DESCRIPTION) solUnimplementedAssert(false, DESCRIPTION)
#define astAssert(CONDITION, DESCRIPTION) \
assertThrow(CONDITION, ::solidity::langutil::InvalidAstError, DESCRIPTION) /// Assertion that throws an InvalidAstError containing the given description if it is not met.
#if !BOOST_PP_VARIADICS_MSVC
#define astAssert(...) BOOST_PP_OVERLOAD(astAssert_,__VA_ARGS__)(__VA_ARGS__)
#else
#define astAssert(...) BOOST_PP_CAT(BOOST_PP_OVERLOAD(astAssert_,__VA_ARGS__)(__VA_ARGS__),BOOST_PP_EMPTY())
#endif
#define astAssert_1(CONDITION) \
astAssert_2(CONDITION, "")
#define astAssert_2(CONDITION, DESCRIPTION) \
assertThrowWithDefaultDescription( \
CONDITION, \
::solidity::langutil::InvalidAstError, \
DESCRIPTION, \
"AST assertion failed" \
)
using errorSourceLocationInfo = std::pair<std::string, SourceLocation>; using errorSourceLocationInfo = std::pair<std::string, SourceLocation>;

View File

@ -60,8 +60,8 @@ void CHCSmtLib2Interface::reset()
void CHCSmtLib2Interface::registerRelation(Expression const& _expr) void CHCSmtLib2Interface::registerRelation(Expression const& _expr)
{ {
smtAssert(_expr.sort, ""); smtAssert(_expr.sort);
smtAssert(_expr.sort->kind == Kind::Function, ""); smtAssert(_expr.sort->kind == Kind::Function);
if (!m_variables.count(_expr.name)) if (!m_variables.count(_expr.name))
{ {
auto fSort = dynamic_pointer_cast<FunctionSort>(_expr.sort); auto fSort = dynamic_pointer_cast<FunctionSort>(_expr.sort);
@ -124,7 +124,7 @@ pair<CheckResult, CHCSolverInterface::CexGraph> CHCSmtLib2Interface::query(Expre
void CHCSmtLib2Interface::declareVariable(string const& _name, SortPointer const& _sort) void CHCSmtLib2Interface::declareVariable(string const& _name, SortPointer const& _sort)
{ {
smtAssert(_sort, ""); smtAssert(_sort);
if (_sort->kind == Kind::Function) if (_sort->kind == Kind::Function)
declareFunction(_name, _sort); declareFunction(_name, _sort);
else if (!m_variables.count(_name)) else if (!m_variables.count(_name))
@ -172,13 +172,13 @@ string CHCSmtLib2Interface::forall()
void CHCSmtLib2Interface::declareFunction(string const& _name, SortPointer const& _sort) void CHCSmtLib2Interface::declareFunction(string const& _name, SortPointer const& _sort)
{ {
smtAssert(_sort, ""); smtAssert(_sort);
smtAssert(_sort->kind == Kind::Function, ""); smtAssert(_sort->kind == Kind::Function);
// TODO Use domain and codomain as key as well // TODO Use domain and codomain as key as well
if (!m_variables.count(_name)) if (!m_variables.count(_name))
{ {
auto fSort = dynamic_pointer_cast<FunctionSort>(_sort); auto fSort = dynamic_pointer_cast<FunctionSort>(_sort);
smtAssert(fSort->codomain, ""); smtAssert(fSort->codomain);
string domain = toSmtLibSort(fSort->domain); string domain = toSmtLibSort(fSort->domain);
string codomain = toSmtLibSort(*fSort->codomain); string codomain = toSmtLibSort(*fSort->codomain);
m_variables.insert(_name); m_variables.insert(_name);

View File

@ -21,12 +21,31 @@
#include <libsolutil/Assertions.h> #include <libsolutil/Assertions.h>
#include <libsolutil/Exceptions.h> #include <libsolutil/Exceptions.h>
#include <boost/preprocessor/cat.hpp>
#include <boost/preprocessor/facilities/empty.hpp>
#include <boost/preprocessor/facilities/overload.hpp>
namespace solidity::smtutil namespace solidity::smtutil
{ {
struct SMTLogicError: virtual util::Exception {}; struct SMTLogicError: virtual util::Exception {};
#define smtAssert(CONDITION, DESCRIPTION) \ /// Assertion that throws an SMTLogicError containing the given description if it is not met.
assertThrow(CONDITION, SMTLogicError, DESCRIPTION) #if !BOOST_PP_VARIADICS_MSVC
#define smtAssert(...) BOOST_PP_OVERLOAD(smtAssert_,__VA_ARGS__)(__VA_ARGS__)
#else
#define smtAssert(...) BOOST_PP_CAT(BOOST_PP_OVERLOAD(smtAssert_,__VA_ARGS__)(__VA_ARGS__),BOOST_PP_EMPTY())
#endif
#define smtAssert_1(CONDITION) \
smtAssert_2(CONDITION, "")
#define smtAssert_2(CONDITION, DESCRIPTION) \
assertThrowWithDefaultDescription( \
CONDITION, \
::solidity::smtutil::SMTLogicError, \
DESCRIPTION, \
"SMT assertion failed" \
)
} }

View File

@ -553,7 +553,7 @@ void ControlFlowBuilder::operator()(yul::FunctionDefinition const&)
void ControlFlowBuilder::operator()(yul::Leave const&) void ControlFlowBuilder::operator()(yul::Leave const&)
{ {
// This has to be implemented, if we ever decide to visit functions. // This has to be implemented, if we ever decide to visit functions.
solUnimplementedAssert(false, ""); solUnimplemented("");
} }
bool ControlFlowBuilder::visit(VariableDeclaration const& _variableDeclaration) bool ControlFlowBuilder::visit(VariableDeclaration const& _variableDeclaration)

View File

@ -61,7 +61,7 @@ map<string, ASTPointer<SourceUnit>> ASTJsonImporter::jsonToSourceUnit(map<string
m_sourceNames.emplace_back(make_shared<string const>(src.first)); m_sourceNames.emplace_back(make_shared<string const>(src.first));
for (auto const& srcPair: _sourceList) for (auto const& srcPair: _sourceList)
{ {
astAssert(!srcPair.second.isNull(), ""); astAssert(!srcPair.second.isNull());
astAssert(member(srcPair.second,"nodeType") == "SourceUnit", "The 'nodeType' of the highest node must be 'SourceUnit'."); astAssert(member(srcPair.second,"nodeType") == "SourceUnit", "The 'nodeType' of the highest node must be 'SourceUnit'.");
m_sourceUnits[srcPair.first] = createSourceUnit(srcPair.second, srcPair.first); m_sourceUnits[srcPair.first] = createSourceUnit(srcPair.second, srcPair.first);
} }
@ -485,17 +485,17 @@ ASTPointer<VariableDeclaration> ASTJsonImporter::createVariableDeclaration(Json:
if (mutabilityStr == "constant") if (mutabilityStr == "constant")
{ {
mutability = VariableDeclaration::Mutability::Constant; mutability = VariableDeclaration::Mutability::Constant;
astAssert(memberAsBool(_node, "constant"), ""); astAssert(memberAsBool(_node, "constant"));
} }
else else
{ {
astAssert(!memberAsBool(_node, "constant"), ""); astAssert(!memberAsBool(_node, "constant"));
if (mutabilityStr == "mutable") if (mutabilityStr == "mutable")
mutability = VariableDeclaration::Mutability::Mutable; mutability = VariableDeclaration::Mutability::Mutable;
else if (mutabilityStr == "immutable") else if (mutabilityStr == "immutable")
mutability = VariableDeclaration::Mutability::Immutable; mutability = VariableDeclaration::Mutability::Immutable;
else else
astAssert(false, ""); astAssert(false);
} }
return createASTNode<VariableDeclaration>( return createASTNode<VariableDeclaration>(

View File

@ -2112,8 +2112,7 @@ bool ExpressionCompiler::visit(IndexRangeAccess const& _indexAccess)
solUnimplementedAssert( solUnimplementedAssert(
arrayType->location() == DataLocation::CallData && arrayType->location() == DataLocation::CallData &&
arrayType->isDynamicallySized() && arrayType->isDynamicallySized() &&
!arrayType->baseType()->isDynamicallyEncoded(), !arrayType->baseType()->isDynamicallyEncoded()
""
); );
if (_indexAccess.startExpression()) if (_indexAccess.startExpression())

View File

@ -155,7 +155,7 @@ ImmutableItem::ImmutableItem(CompilerContext& _compilerContext, VariableDeclarat
void ImmutableItem::retrieveValue(SourceLocation const&, bool) const void ImmutableItem::retrieveValue(SourceLocation const&, bool) const
{ {
solUnimplementedAssert(m_dataType->isValueType(), ""); solUnimplementedAssert(m_dataType->isValueType());
if (m_context.runtimeContext()) if (m_context.runtimeContext())
CompilerUtils(m_context).loadFromMemory( CompilerUtils(m_context).loadFromMemory(
@ -172,7 +172,7 @@ void ImmutableItem::retrieveValue(SourceLocation const&, bool) const
void ImmutableItem::storeValue(Type const& _sourceType, SourceLocation const&, bool _move) const void ImmutableItem::storeValue(Type const& _sourceType, SourceLocation const&, bool _move) const
{ {
CompilerUtils utils(m_context); CompilerUtils utils(m_context);
solUnimplementedAssert(m_dataType->isValueType(), ""); solUnimplementedAssert(m_dataType->isValueType());
solAssert(_sourceType.isValueType(), ""); solAssert(_sourceType.isValueType(), "");
utils.convertType(_sourceType, *m_dataType, true); utils.convertType(_sourceType, *m_dataType, true);

View File

@ -1217,7 +1217,7 @@ string YulUtilFunctions::extractByteArrayLengthFunction()
std::string YulUtilFunctions::resizeArrayFunction(ArrayType const& _type) std::string YulUtilFunctions::resizeArrayFunction(ArrayType const& _type)
{ {
solAssert(_type.location() == DataLocation::Storage, ""); solAssert(_type.location() == DataLocation::Storage, "");
solUnimplementedAssert(_type.baseType()->storageBytes() <= 32, "..."); solUnimplementedAssert(_type.baseType()->storageBytes() <= 32);
if (_type.isByteArray()) if (_type.isByteArray())
return resizeDynamicByteArrayFunction(_type); return resizeDynamicByteArrayFunction(_type);
@ -1259,7 +1259,7 @@ string YulUtilFunctions::cleanUpStorageArrayEndFunction(ArrayType const& _type)
solAssert(_type.location() == DataLocation::Storage, ""); solAssert(_type.location() == DataLocation::Storage, "");
solAssert(_type.baseType()->category() != Type::Category::Mapping, ""); solAssert(_type.baseType()->category() != Type::Category::Mapping, "");
solAssert(!_type.isByteArray(), ""); solAssert(!_type.isByteArray(), "");
solUnimplementedAssert(_type.baseType()->storageBytes() <= 32, ""); solUnimplementedAssert(_type.baseType()->storageBytes() <= 32);
string functionName = "cleanup_storage_array_end_" + _type.identifier(); string functionName = "cleanup_storage_array_end_" + _type.identifier();
return m_functionCollector.createFunction(functionName, [&](vector<string>& _args, vector<string>&) { return m_functionCollector.createFunction(functionName, [&](vector<string>& _args, vector<string>&) {
@ -1555,7 +1555,7 @@ string YulUtilFunctions::storageArrayPushFunction(ArrayType const& _type, Type c
if (!_fromType) if (!_fromType)
_fromType = _type.baseType(); _fromType = _type.baseType();
else if (_fromType->isValueType()) else if (_fromType->isValueType())
solUnimplementedAssert(*_fromType == *_type.baseType(), ""); solUnimplementedAssert(*_fromType == *_type.baseType());
string functionName = string functionName =
string{"array_push_from_"} + string{"array_push_from_"} +
@ -3304,10 +3304,10 @@ string YulUtilFunctions::conversionFunction(Type const& _from, Type const& _to)
if (auto const* toFixedBytes = dynamic_cast<FixedBytesType const*>(&_to)) if (auto const* toFixedBytes = dynamic_cast<FixedBytesType const*>(&_to))
convert = shiftLeftFunction(256 - toFixedBytes->numBytes() * 8); convert = shiftLeftFunction(256 - toFixedBytes->numBytes() * 8);
else if (dynamic_cast<FixedPointType const*>(&_to)) else if (dynamic_cast<FixedPointType const*>(&_to))
solUnimplementedAssert(false, ""); solUnimplemented("");
else if (dynamic_cast<IntegerType const*>(&_to)) else if (dynamic_cast<IntegerType const*>(&_to))
{ {
solUnimplementedAssert(fromCategory != Type::Category::FixedPoint, ""); solUnimplementedAssert(fromCategory != Type::Category::FixedPoint);
convert = identityFunction(); convert = identityFunction();
} }
else if (toCategory == Type::Category::Enum) else if (toCategory == Type::Category::Enum)
@ -3346,8 +3346,8 @@ string YulUtilFunctions::conversionFunction(Type const& _from, Type const& _to)
body = "converted := value"; body = "converted := value";
else else
{ {
solUnimplementedAssert(toStructType.location() == DataLocation::Memory, ""); solUnimplementedAssert(toStructType.location() == DataLocation::Memory);
solUnimplementedAssert(fromStructType.location() != DataLocation::Memory, ""); solUnimplementedAssert(fromStructType.location() != DataLocation::Memory);
if (fromStructType.location() == DataLocation::CallData) if (fromStructType.location() == DataLocation::CallData)
body = Whiskers(R"( body = Whiskers(R"(
@ -3416,7 +3416,7 @@ string YulUtilFunctions::conversionFunction(Type const& _from, Type const& _to)
} }
case Type::Category::Tuple: case Type::Category::Tuple:
{ {
solUnimplementedAssert(false, "Tuple conversion not implemented."); solUnimplemented("Tuple conversion not implemented.");
break; break;
} }
case Type::Category::TypeType: case Type::Category::TypeType:
@ -4107,7 +4107,7 @@ string YulUtilFunctions::zeroValueFunction(Type const& _type, bool _splitFunctio
else if (auto const* structType = dynamic_cast<StructType const*>(&_type)) else if (auto const* structType = dynamic_cast<StructType const*>(&_type))
templ("zeroValue", allocateAndInitializeMemoryStructFunction(*structType) + "()"); templ("zeroValue", allocateAndInitializeMemoryStructFunction(*structType) + "()");
else else
solUnimplementedAssert(false, ""); solUnimplemented("");
} }
return templ.render(); return templ.render();

View File

@ -544,7 +544,7 @@ string IRGenerator::generateGetter(VariableDeclaration const& _varDecl)
if (_varDecl.immutable()) if (_varDecl.immutable())
{ {
solAssert(paramTypes.empty(), ""); solAssert(paramTypes.empty(), "");
solUnimplementedAssert(type->sizeOnStack() == 1, ""); solUnimplementedAssert(type->sizeOnStack() == 1);
return Whiskers(R"( return Whiskers(R"(
/// @ast-id <astID> /// @ast-id <astID>
<sourceLocationComment> <sourceLocationComment>
@ -925,8 +925,8 @@ string IRGenerator::deployCode(ContractDefinition const& _contract)
else else
for (VariableDeclaration const* immutable: ContractType(_contract).immutableVariables()) for (VariableDeclaration const* immutable: ContractType(_contract).immutableVariables())
{ {
solUnimplementedAssert(immutable->type()->isValueType(), ""); solUnimplementedAssert(immutable->type()->isValueType());
solUnimplementedAssert(immutable->type()->sizeOnStack() == 1, ""); solUnimplementedAssert(immutable->type()->sizeOnStack() == 1);
immutables.emplace_back(map<string, string>{ immutables.emplace_back(map<string, string>{
{"immutableName"s, to_string(immutable->id())}, {"immutableName"s, to_string(immutable->id())},
{"value"s, "mload(" + to_string(m_context.immutableMemoryOffset(*immutable)) + ")"} {"value"s, "mload(" + to_string(m_context.immutableMemoryOffset(*immutable)) + ")"}

View File

@ -108,7 +108,7 @@ private:
{ {
auto const& reference = m_references.at(&_identifier); auto const& reference = m_references.at(&_identifier);
auto const varDecl = dynamic_cast<VariableDeclaration const*>(reference.declaration); auto const varDecl = dynamic_cast<VariableDeclaration const*>(reference.declaration);
solUnimplementedAssert(varDecl, ""); solUnimplementedAssert(varDecl);
string const& suffix = reference.suffix; string const& suffix = reference.suffix;
string value; string value;
@ -737,7 +737,7 @@ bool IRGeneratorForStatements::visit(UnaryOperation const& _unaryOperation)
) << "(" << IRVariable(_unaryOperation.subExpression()).name() << ")\n"; ) << "(" << IRVariable(_unaryOperation.subExpression()).name() << ")\n";
} }
else else
solUnimplementedAssert(false, "Unary operator not yet implemented"); solUnimplemented("Unary operator not yet implemented");
} }
else if (resultType.category() == Type::Category::FixedBytes) else if (resultType.category() == Type::Category::FixedBytes)
{ {
@ -755,7 +755,7 @@ bool IRGeneratorForStatements::visit(UnaryOperation const& _unaryOperation)
appendSimpleUnaryOperation(_unaryOperation, _unaryOperation.subExpression()); appendSimpleUnaryOperation(_unaryOperation, _unaryOperation.subExpression());
} }
else else
solUnimplementedAssert(false, "Unary operator not yet implemented"); solUnimplemented("Unary operator not yet implemented");
return false; return false;
} }
@ -1546,7 +1546,7 @@ void IRGeneratorForStatements::endVisit(FunctionCallOptions const& _options)
setLocation(_options); setLocation(_options);
FunctionType const& previousType = dynamic_cast<FunctionType const&>(*_options.expression().annotation().type); FunctionType const& previousType = dynamic_cast<FunctionType const&>(*_options.expression().annotation().type);
solUnimplementedAssert(!previousType.bound(), ""); solUnimplementedAssert(!previousType.bound());
// Copy over existing values. // Copy over existing values.
for (auto const& item: previousType.stackItems()) for (auto const& item: previousType.stackItems())
@ -1716,7 +1716,7 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess)
{ {
solUnimplementedAssert( solUnimplementedAssert(
dynamic_cast<FunctionType const&>(*_memberAccess.expression().annotation().type).kind() == dynamic_cast<FunctionType const&>(*_memberAccess.expression().annotation().type).kind() ==
FunctionType::Kind::External, "" FunctionType::Kind::External
); );
define(IRVariable{_memberAccess}, IRVariable(_memberAccess.expression()).part("address")); define(IRVariable{_memberAccess}, IRVariable(_memberAccess.expression()).part("address"));
} }
@ -2264,7 +2264,7 @@ void IRGeneratorForStatements::endVisit(IndexRangeAccess const& _indexRangeAcces
break; break;
} }
default: default:
solUnimplementedAssert(false, "Index range accesses is implemented only on calldata arrays."); solUnimplemented("Index range accesses is implemented only on calldata arrays.");
} }
} }
@ -2910,8 +2910,8 @@ void IRGeneratorForStatements::writeToLValue(IRLValue const& _lvalue, IRVariable
[&](IRLValue::Stack const& _stack) { assign(_stack.variable, _value); }, [&](IRLValue::Stack const& _stack) { assign(_stack.variable, _value); },
[&](IRLValue::Immutable const& _immutable) [&](IRLValue::Immutable const& _immutable)
{ {
solUnimplementedAssert(_lvalue.type.isValueType(), ""); solUnimplementedAssert(_lvalue.type.isValueType());
solUnimplementedAssert(_lvalue.type.sizeOnStack() == 1, ""); solUnimplementedAssert(_lvalue.type.sizeOnStack() == 1);
solAssert(_lvalue.type == *_immutable.variable->type(), ""); solAssert(_lvalue.type == *_immutable.variable->type(), "");
size_t memOffset = m_context.immutableMemoryOffset(*_immutable.variable); size_t memOffset = m_context.immutableMemoryOffset(*_immutable.variable);
@ -2970,8 +2970,8 @@ IRVariable IRGeneratorForStatements::readFromLValue(IRLValue const& _lvalue)
define(result, _stack.variable); define(result, _stack.variable);
}, },
[&](IRLValue::Immutable const& _immutable) { [&](IRLValue::Immutable const& _immutable) {
solUnimplementedAssert(_lvalue.type.isValueType(), ""); solUnimplementedAssert(_lvalue.type.isValueType());
solUnimplementedAssert(_lvalue.type.sizeOnStack() == 1, ""); solUnimplementedAssert(_lvalue.type.sizeOnStack() == 1);
solAssert(_lvalue.type == *_immutable.variable->type(), ""); solAssert(_lvalue.type == *_immutable.variable->type(), "");
if (m_context.executionContext() == IRGenerationContext::ExecutionContext::Creation) if (m_context.executionContext() == IRGenerationContext::ExecutionContext::Creation)
{ {

View File

@ -253,7 +253,7 @@ void CompilerStack::setRevertStringBehaviour(RevertStrings _revertStrings)
{ {
if (m_stackState >= ParsedAndImported) if (m_stackState >= ParsedAndImported)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Must set revert string settings before parsing.")); BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Must set revert string settings before parsing."));
solUnimplementedAssert(_revertStrings != RevertStrings::VerboseDebug, ""); solUnimplementedAssert(_revertStrings != RevertStrings::VerboseDebug);
m_revertStrings = _revertStrings; m_revertStrings = _revertStrings;
} }

View File

@ -27,6 +27,8 @@
#include <libsolutil/Exceptions.h> #include <libsolutil/Exceptions.h>
#include <string>
namespace solidity::util namespace solidity::util
{ {
@ -38,16 +40,32 @@ namespace solidity::util
#define ETH_FUNC __func__ #define ETH_FUNC __func__
#endif #endif
/// Assertion that throws an exception containing the given description if it is not met. namespace assertions
/// Use it as assertThrow(1 == 1, ExceptionType, "Mathematics is wrong."); {
/// Do NOT supply an exception object as the second parameter.
#define assertThrow(_condition, _exceptionType, _description) \ inline std::string stringOrDefault(std::string _string, std::string _defaultString)
{
// NOTE: Putting this in a function rather than directly in a macro prevents the string from
// being evaluated multiple times if it's not just a literal.
return (!_string.empty() ? _string : _defaultString);
}
}
/// Base macro that can be used to implement assertion macros.
/// Throws an exception containing the given description if the condition is not met.
/// Allows you to provide the default description for the case where the user of your macro does
/// not provide any.
/// The second parameter must be an exception class (rather than an instance).
#define assertThrowWithDefaultDescription(_condition, _exceptionType, _description, _defaultDescription) \
do \ do \
{ \ { \
if (!(_condition)) \ if (!(_condition)) \
::boost::throw_exception( \ ::boost::throw_exception( \
_exceptionType() << \ _exceptionType() << \
::solidity::util::errinfo_comment(_description) << \ ::solidity::util::errinfo_comment( \
::solidity::util::assertions::stringOrDefault(_description, _defaultDescription) \
) << \
::boost::throw_function(ETH_FUNC) << \ ::boost::throw_function(ETH_FUNC) << \
::boost::throw_file(__FILE__) << \ ::boost::throw_file(__FILE__) << \
::boost::throw_line(__LINE__) \ ::boost::throw_line(__LINE__) \
@ -55,4 +73,10 @@ namespace solidity::util
} \ } \
while (false) while (false)
/// Assertion that throws an exception containing the given description if it is not met.
/// Use it as assertThrow(1 == 1, ExceptionType, "Mathematics is wrong.");
/// The second parameter must be an exception class (rather than an instance).
#define assertThrow(_condition, _exceptionType, _description) \
assertThrowWithDefaultDescription(_condition, _exceptionType, _description, "Assertion failed")
} }

View File

@ -26,6 +26,10 @@
#include <libyul/YulString.h> #include <libyul/YulString.h>
#include <boost/preprocessor/cat.hpp>
#include <boost/preprocessor/facilities/empty.hpp>
#include <boost/preprocessor/facilities/overload.hpp>
namespace solidity::yul namespace solidity::yul
{ {
@ -52,7 +56,21 @@ struct StackTooDeepError: virtual YulException
}; };
/// Assertion that throws an YulAssertion containing the given description if it is not met. /// Assertion that throws an YulAssertion containing the given description if it is not met.
#define yulAssert(CONDITION, DESCRIPTION) \ #if !BOOST_PP_VARIADICS_MSVC
assertThrow(CONDITION, ::solidity::yul::YulAssertion, DESCRIPTION) #define yulAssert(...) BOOST_PP_OVERLOAD(yulAssert_,__VA_ARGS__)(__VA_ARGS__)
#else
#define yulAssert(...) BOOST_PP_CAT(BOOST_PP_OVERLOAD(yulAssert_,__VA_ARGS__)(__VA_ARGS__),BOOST_PP_EMPTY())
#endif
#define yulAssert_1(CONDITION) \
yulAssert_2(CONDITION, "")
#define yulAssert_2(CONDITION, DESCRIPTION) \
assertThrowWithDefaultDescription( \
CONDITION, \
::solidity::yul::YulAssertion, \
DESCRIPTION, \
"Yul assertion failed" \
)
} }

View File

@ -1259,7 +1259,7 @@ bool CommandLineParser::processArgs()
if (m_options.input.mode == InputMode::Compiler) if (m_options.input.mode == InputMode::Compiler)
m_options.input.errorRecovery = (m_args.count(g_strErrorRecovery) > 0); m_options.input.errorRecovery = (m_args.count(g_strErrorRecovery) > 0);
solAssert(m_options.input.mode == InputMode::Compiler || m_options.input.mode == InputMode::CompilerWithASTImport, ""); solAssert(m_options.input.mode == InputMode::Compiler || m_options.input.mode == InputMode::CompilerWithASTImport);
return true; return true;
} }

View File

@ -118,7 +118,7 @@ public:
size_t _columns size_t _columns
) )
{ {
yulAssert(_columns > 0, ""); yulAssert(_columns > 0);
auto hasShorterString = [](auto const& a, auto const& b) { return a.second.size() < b.second.size(); }; auto hasShorterString = [](auto const& a, auto const& b) { return a.second.size() < b.second.size(); };
size_t longestDescriptionLength = std::max( size_t longestDescriptionLength = std::max(
@ -151,7 +151,7 @@ public:
); );
}); });
yulAssert(sortedOptions.size() > 0, ""); yulAssert(sortedOptions.size() > 0);
size_t rows = (sortedOptions.size() - 1) / _columns + 1; size_t rows = (sortedOptions.size() - 1) / _columns + 1;
for (size_t row = 0; row < rows; ++row) for (size_t row = 0; row < rows; ++row)
{ {