mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge remote-tracking branch 'origin/develop' into breaking
This commit is contained in:
@@ -166,7 +166,7 @@ if (NOT (${Z3_FOUND} OR ${CVC4_FOUND}))
|
||||
endif()
|
||||
|
||||
add_library(solidity ${sources} ${z3_SRCS} ${cvc4_SRCS})
|
||||
target_link_libraries(solidity PUBLIC yul evmasm langutil solutil Boost::boost Boost::filesystem Boost::system)
|
||||
target_link_libraries(solidity PUBLIC yul evmasm langutil solutil Boost::boost)
|
||||
|
||||
if (${Z3_FOUND})
|
||||
target_link_libraries(solidity PUBLIC z3::libz3)
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
using namespace std;
|
||||
using namespace solidity;
|
||||
using namespace solidity::frontend;
|
||||
using namespace solidity::langutil;
|
||||
|
||||
void ConstantEvaluator::endVisit(UnaryOperation const& _operation)
|
||||
{
|
||||
@@ -46,6 +47,7 @@ void ConstantEvaluator::endVisit(BinaryOperation const& _operation)
|
||||
TypePointer commonType = left->binaryOperatorResult(_operation.getOperator(), right);
|
||||
if (!commonType)
|
||||
m_errorReporter.fatalTypeError(
|
||||
6020_error,
|
||||
_operation.location(),
|
||||
"Operator " +
|
||||
string(TokenTraits::toString(_operation.getOperator())) +
|
||||
@@ -82,7 +84,7 @@ void ConstantEvaluator::endVisit(Identifier const& _identifier)
|
||||
else if (!m_types->count(value.get()))
|
||||
{
|
||||
if (m_depth > 32)
|
||||
m_errorReporter.fatalTypeError(_identifier.location(), "Cyclic constant definition (or maximum recursion depth exhausted).");
|
||||
m_errorReporter.fatalTypeError(5210_error, _identifier.location(), "Cyclic constant definition (or maximum recursion depth exhausted).");
|
||||
ConstantEvaluator(m_errorReporter, m_depth + 1, m_types).evaluate(*value);
|
||||
}
|
||||
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
#include <libsolidity/analysis/TypeChecker.h>
|
||||
#include <liblangutil/ErrorReporter.h>
|
||||
#include <boost/range/adaptor/reversed.hpp>
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
|
||||
|
||||
using namespace std;
|
||||
@@ -78,6 +77,7 @@ void ContractLevelChecker::checkDuplicateFunctions(ContractDefinition const& _co
|
||||
{
|
||||
if (constructor)
|
||||
m_errorReporter.declarationError(
|
||||
7997_error,
|
||||
function->location(),
|
||||
SecondarySourceLocation().append("Another declaration is here:", constructor->location()),
|
||||
"More than one constructor defined."
|
||||
@@ -88,6 +88,7 @@ void ContractLevelChecker::checkDuplicateFunctions(ContractDefinition const& _co
|
||||
{
|
||||
if (fallback)
|
||||
m_errorReporter.declarationError(
|
||||
7301_error,
|
||||
function->location(),
|
||||
SecondarySourceLocation().append("Another declaration is here:", fallback->location()),
|
||||
"Only one fallback function is allowed."
|
||||
@@ -98,6 +99,7 @@ void ContractLevelChecker::checkDuplicateFunctions(ContractDefinition const& _co
|
||||
{
|
||||
if (receive)
|
||||
m_errorReporter.declarationError(
|
||||
4046_error,
|
||||
function->location(),
|
||||
SecondarySourceLocation().append("Another declaration is here:", receive->location()),
|
||||
"Only one receive function is allowed."
|
||||
@@ -110,7 +112,7 @@ void ContractLevelChecker::checkDuplicateFunctions(ContractDefinition const& _co
|
||||
functions[function->name()].push_back(function);
|
||||
}
|
||||
|
||||
findDuplicateDefinitions(functions, "Function with same name and arguments defined twice.");
|
||||
findDuplicateDefinitions(functions);
|
||||
}
|
||||
|
||||
void ContractLevelChecker::checkDuplicateEvents(ContractDefinition const& _contract)
|
||||
@@ -121,11 +123,11 @@ void ContractLevelChecker::checkDuplicateEvents(ContractDefinition const& _contr
|
||||
for (EventDefinition const* event: _contract.events())
|
||||
events[event->name()].push_back(event);
|
||||
|
||||
findDuplicateDefinitions(events, "Event with same name and arguments defined twice.");
|
||||
findDuplicateDefinitions(events);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void ContractLevelChecker::findDuplicateDefinitions(map<string, vector<T>> const& _definitions, string _message)
|
||||
void ContractLevelChecker::findDuplicateDefinitions(map<string, vector<T>> const& _definitions)
|
||||
{
|
||||
for (auto const& it: _definitions)
|
||||
{
|
||||
@@ -144,12 +146,27 @@ void ContractLevelChecker::findDuplicateDefinitions(map<string, vector<T>> const
|
||||
|
||||
if (ssl.infos.size() > 0)
|
||||
{
|
||||
ssl.limitSize(_message);
|
||||
ErrorId error;
|
||||
string message;
|
||||
if constexpr (is_same_v<T, FunctionDefinition const*>)
|
||||
{
|
||||
error = 1686_error;
|
||||
message = "Function with same name and arguments defined twice.";
|
||||
}
|
||||
else
|
||||
{
|
||||
static_assert(is_same_v<T, EventDefinition const*>, "Expected \"FunctionDefinition const*\" or \"EventDefinition const*\"");
|
||||
error = 5883_error;
|
||||
message = "Event with same name and arguments defined twice.";
|
||||
}
|
||||
|
||||
ssl.limitSize(message);
|
||||
|
||||
m_errorReporter.declarationError(
|
||||
error,
|
||||
overloads[i]->location(),
|
||||
ssl,
|
||||
_message
|
||||
message
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -197,9 +214,9 @@ void ContractLevelChecker::checkAbstractDefinitions(ContractDefinition const& _c
|
||||
if (_contract.abstract())
|
||||
{
|
||||
if (_contract.contractKind() == ContractKind::Interface)
|
||||
m_errorReporter.typeError(_contract.location(), "Interfaces do not need the \"abstract\" keyword, they are abstract implicitly.");
|
||||
m_errorReporter.typeError(9348_error, _contract.location(), "Interfaces do not need the \"abstract\" keyword, they are abstract implicitly.");
|
||||
else if (_contract.contractKind() == ContractKind::Library)
|
||||
m_errorReporter.typeError(_contract.location(), "Libraries cannot be abstract.");
|
||||
m_errorReporter.typeError(9571_error, _contract.location(), "Libraries cannot be abstract.");
|
||||
else
|
||||
solAssert(_contract.contractKind() == ContractKind::Contract, "");
|
||||
}
|
||||
@@ -215,10 +232,12 @@ void ContractLevelChecker::checkAbstractDefinitions(ContractDefinition const& _c
|
||||
SecondarySourceLocation ssl;
|
||||
for (auto declaration: _contract.annotation().unimplementedDeclarations)
|
||||
ssl.append("Missing implementation: ", declaration->location());
|
||||
m_errorReporter.typeError(_contract.location(), ssl,
|
||||
"Contract \"" + _contract.annotation().canonicalName
|
||||
+ "\" should be marked as abstract.");
|
||||
|
||||
m_errorReporter.typeError(
|
||||
3656_error,
|
||||
_contract.location(),
|
||||
ssl,
|
||||
"Contract \"" + _contract.annotation().canonicalName + "\" should be marked as abstract."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -243,6 +262,7 @@ void ContractLevelChecker::checkBaseConstructorArguments(ContractDefinition cons
|
||||
}
|
||||
else
|
||||
m_errorReporter.declarationError(
|
||||
1563_error,
|
||||
modifier->location(),
|
||||
"Modifier-style base constructor call without arguments."
|
||||
);
|
||||
@@ -304,6 +324,7 @@ void ContractLevelChecker::annotateBaseConstructorArguments(
|
||||
}
|
||||
|
||||
m_errorReporter.declarationError(
|
||||
3364_error,
|
||||
*mainLocation,
|
||||
ssl,
|
||||
"Base constructor arguments given twice."
|
||||
@@ -343,6 +364,7 @@ void ContractLevelChecker::checkExternalTypeClashes(ContractDefinition const& _c
|
||||
for (size_t j = i + 1; j < it.second.size(); ++j)
|
||||
if (!it.second[i].second->hasEqualParameterTypes(*it.second[j].second))
|
||||
m_errorReporter.typeError(
|
||||
9914_error,
|
||||
it.second[j].first->location(),
|
||||
"Function overload clash during conversion to external types for arguments."
|
||||
);
|
||||
@@ -356,6 +378,7 @@ void ContractLevelChecker::checkHashCollisions(ContractDefinition const& _contra
|
||||
util::FixedHash<4> const& hash = it.first;
|
||||
if (hashes.count(hash))
|
||||
m_errorReporter.typeError(
|
||||
1860_error,
|
||||
_contract.location(),
|
||||
string("Function signature hash collision for ") + it.second->externalSignature()
|
||||
);
|
||||
@@ -369,11 +392,11 @@ void ContractLevelChecker::checkLibraryRequirements(ContractDefinition const& _c
|
||||
return;
|
||||
|
||||
if (!_contract.baseContracts().empty())
|
||||
m_errorReporter.typeError(_contract.location(), "Library is not allowed to inherit.");
|
||||
m_errorReporter.typeError(9469_error, _contract.location(), "Library is not allowed to inherit.");
|
||||
|
||||
for (auto const& var: _contract.stateVariables())
|
||||
if (!var->isConstant())
|
||||
m_errorReporter.typeError(var->location(), "Library cannot have non-constant state variables");
|
||||
m_errorReporter.typeError(9957_error, var->location(), "Library cannot have non-constant state variables");
|
||||
}
|
||||
|
||||
void ContractLevelChecker::checkBaseABICompatibility(ContractDefinition const& _contract)
|
||||
@@ -412,6 +435,7 @@ void ContractLevelChecker::checkBaseABICompatibility(ContractDefinition const& _
|
||||
|
||||
if (!errors.infos.empty())
|
||||
m_errorReporter.fatalTypeError(
|
||||
6594_error,
|
||||
_contract.location(),
|
||||
errors,
|
||||
std::string("Contract \"") +
|
||||
@@ -428,6 +452,7 @@ void ContractLevelChecker::checkPayableFallbackWithoutReceive(ContractDefinition
|
||||
if (auto const* fallback = _contract.fallbackFunction())
|
||||
if (fallback->isPayable() && !_contract.interfaceFunctionList().empty() && !_contract.receiveFunction())
|
||||
m_errorReporter.warning(
|
||||
3628_error,
|
||||
_contract.location(),
|
||||
"This contract has a payable fallback function, but no receive ether function. Consider adding a receive ether function.",
|
||||
SecondarySourceLocation{}.append("The payable fallback function is defined here.", fallback->location())
|
||||
|
||||
@@ -60,7 +60,7 @@ private:
|
||||
void checkDuplicateFunctions(ContractDefinition const& _contract);
|
||||
void checkDuplicateEvents(ContractDefinition const& _contract);
|
||||
template <class T>
|
||||
void findDuplicateDefinitions(std::map<std::string, std::vector<T>> const& _definitions, std::string _message);
|
||||
void findDuplicateDefinitions(std::map<std::string, std::vector<T>> const& _definitions);
|
||||
/// Checks for unimplemented functions and modifiers.
|
||||
void checkAbstractDefinitions(ContractDefinition const& _contract);
|
||||
/// Checks that the base constructor arguments are properly provided.
|
||||
|
||||
@@ -136,6 +136,7 @@ void ControlFlowAnalyzer::checkUninitializedAccess(CFGNode const* _entry, CFGNod
|
||||
ssl.append("The variable was declared here.", variableOccurrence->declaration().location());
|
||||
|
||||
m_errorReporter.typeError(
|
||||
3464_error,
|
||||
variableOccurrence->occurrence() ?
|
||||
*variableOccurrence->occurrence() :
|
||||
variableOccurrence->declaration().location(),
|
||||
@@ -176,6 +177,6 @@ void ControlFlowAnalyzer::checkUnreachable(CFGNode const* _entry, CFGNode const*
|
||||
// Extend the location, as long as the next location overlaps (unreachable is sorted).
|
||||
for (; it != unreachable.end() && it->start <= location.end; ++it)
|
||||
location.end = std::max(location.end, it->end);
|
||||
m_errorReporter.warning(location, "Unreachable code.");
|
||||
m_errorReporter.warning(5740_error, location, "Unreachable code.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
#include <libsolidity/analysis/ControlFlowGraph.h>
|
||||
|
||||
#include <libsolidity/analysis/ControlFlowBuilder.h>
|
||||
#include <boost/range/adaptor/reversed.hpp>
|
||||
#include <algorithm>
|
||||
|
||||
using namespace std;
|
||||
|
||||
@@ -188,12 +188,14 @@ void DeclarationTypeChecker::endVisit(Mapping const& _mapping)
|
||||
{
|
||||
if (contractType->contractDefinition().isLibrary())
|
||||
m_errorReporter.fatalTypeError(
|
||||
1665_error,
|
||||
typeName->location(),
|
||||
"Library types cannot be used as mapping keys."
|
||||
);
|
||||
}
|
||||
else if (typeName->annotation().type->category() != Type::Category::Enum)
|
||||
m_errorReporter.fatalTypeError(
|
||||
7804_error,
|
||||
typeName->location(),
|
||||
"Only elementary types, contract types or enums are allowed as mapping keys."
|
||||
);
|
||||
@@ -253,9 +255,9 @@ void DeclarationTypeChecker::endVisit(VariableDeclaration const& _variable)
|
||||
return;
|
||||
|
||||
if (_variable.isConstant() && !_variable.isStateVariable())
|
||||
m_errorReporter.declarationError(_variable.location(), "The \"constant\" keyword can only be used for state variables.");
|
||||
m_errorReporter.declarationError(1788_error, _variable.location(), "The \"constant\" keyword can only be used for state variables.");
|
||||
if (_variable.immutable() && !_variable.isStateVariable())
|
||||
m_errorReporter.declarationError(_variable.location(), "The \"immutable\" keyword can only be used for state variables.");
|
||||
m_errorReporter.declarationError(8297_error, _variable.location(), "The \"immutable\" keyword can only be used for state variables.");
|
||||
|
||||
if (!_variable.typeName())
|
||||
{
|
||||
@@ -360,19 +362,19 @@ void DeclarationTypeChecker::endVisit(VariableDeclaration const& _variable)
|
||||
void DeclarationTypeChecker::typeError(SourceLocation const& _location, string const& _description)
|
||||
{
|
||||
m_errorOccurred = true;
|
||||
m_errorReporter.typeError(_location, _description);
|
||||
m_errorReporter.typeError(2311_error, _location, _description);
|
||||
}
|
||||
|
||||
void DeclarationTypeChecker::fatalTypeError(SourceLocation const& _location, string const& _description)
|
||||
{
|
||||
m_errorOccurred = true;
|
||||
m_errorReporter.fatalTypeError(_location, _description);
|
||||
m_errorReporter.fatalTypeError(5651_error, _location, _description);
|
||||
}
|
||||
|
||||
void DeclarationTypeChecker::fatalDeclarationError(SourceLocation const& _location, string const& _description)
|
||||
{
|
||||
m_errorOccurred = true;
|
||||
m_errorReporter.fatalDeclarationError(_location, _description);
|
||||
m_errorReporter.fatalDeclarationError(2046_error, _location, _description);
|
||||
}
|
||||
|
||||
bool DeclarationTypeChecker::check(ASTNode const& _node)
|
||||
|
||||
@@ -173,5 +173,5 @@ void DocStringAnalyser::parseDocStrings(
|
||||
void DocStringAnalyser::appendError(SourceLocation const& _location, string const& _description)
|
||||
{
|
||||
m_errorOccured = true;
|
||||
m_errorReporter.docstringParsingError(_location, _description);
|
||||
m_errorReporter.docstringParsingError(7816_error, _location, _description);
|
||||
}
|
||||
|
||||
@@ -100,11 +100,13 @@ inline vector<shared_ptr<MagicVariableDeclaration const>> constructMagicVariable
|
||||
magicVarDecl("sha3", TypeProvider::function(strings{"bytes memory"}, strings{"bytes32"}, FunctionType::Kind::KECCAK256, false, StateMutability::Pure)),
|
||||
magicVarDecl("suicide", TypeProvider::function(strings{"address payable"}, strings{}, FunctionType::Kind::Selfdestruct)),
|
||||
magicVarDecl("tx", TypeProvider::magic(MagicType::Kind::Transaction)),
|
||||
// Accepts a MagicType that can be any contract type or an Integer type and returns a
|
||||
// MagicType. The TypeChecker handles the correctness of the input and output types.
|
||||
magicVarDecl("type", TypeProvider::function(
|
||||
strings{"address"} /* accepts any contract type, handled by the type checker */,
|
||||
strings{} /* returns a MagicType, handled by the type checker */,
|
||||
strings{},
|
||||
strings{},
|
||||
FunctionType::Kind::MetaType,
|
||||
false,
|
||||
true,
|
||||
StateMutability::Pure
|
||||
)),
|
||||
};
|
||||
@@ -123,7 +125,7 @@ vector<Declaration const*> GlobalContext::declarations() const
|
||||
{
|
||||
vector<Declaration const*> declarations;
|
||||
declarations.reserve(m_magicVariables.size());
|
||||
for (ASTPointer<Declaration const> const& variable: m_magicVariables)
|
||||
for (ASTPointer<MagicVariableDeclaration const> const& variable: m_magicVariables)
|
||||
declarations.push_back(variable.get());
|
||||
return declarations;
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include <boost/range/adaptor/reversed.hpp>
|
||||
|
||||
using namespace solidity::frontend;
|
||||
using namespace solidity::langutil;
|
||||
|
||||
void ImmutableValidator::analyze()
|
||||
{
|
||||
@@ -42,7 +43,7 @@ void ImmutableValidator::analyze()
|
||||
visitCallableIfNew(*contract->constructor());
|
||||
|
||||
for (ContractDefinition const* contract: linearizedContracts)
|
||||
for (std::shared_ptr<InheritanceSpecifier> const inheritSpec: contract->baseContracts())
|
||||
for (std::shared_ptr<InheritanceSpecifier> const& inheritSpec: contract->baseContracts())
|
||||
if (auto args = inheritSpec->arguments())
|
||||
ASTNode::listAccept(*args, *this);
|
||||
|
||||
@@ -165,33 +166,39 @@ void ImmutableValidator::analyseVariableReference(VariableDeclaration const& _va
|
||||
{
|
||||
if (!m_currentConstructor)
|
||||
m_errorReporter.typeError(
|
||||
1581_error,
|
||||
_expression.location(),
|
||||
"Immutable variables can only be initialized inline or assigned directly in the constructor."
|
||||
);
|
||||
else if (m_currentConstructor->annotation().contract->id() != _variableReference.annotation().contract->id())
|
||||
m_errorReporter.typeError(
|
||||
7484_error,
|
||||
_expression.location(),
|
||||
"Immutable variables must be initialized in the constructor of the contract they are defined in."
|
||||
);
|
||||
else if (m_inLoop)
|
||||
m_errorReporter.typeError(
|
||||
6672_error,
|
||||
_expression.location(),
|
||||
"Immutable variables can only be initialized once, not in a while statement."
|
||||
);
|
||||
else if (m_inBranch)
|
||||
m_errorReporter.typeError(
|
||||
4599_error,
|
||||
_expression.location(),
|
||||
"Immutable variables must be initialized unconditionally, not in an if statement."
|
||||
);
|
||||
|
||||
if (!m_initializedStateVariables.emplace(&_variableReference).second)
|
||||
m_errorReporter.typeError(
|
||||
1574_error,
|
||||
_expression.location(),
|
||||
"Immutable state variable already initialized."
|
||||
);
|
||||
}
|
||||
else if (m_inConstructionContext)
|
||||
m_errorReporter.typeError(
|
||||
7733_error,
|
||||
_expression.location(),
|
||||
"Immutable variables cannot be read during contract creation time, which means "
|
||||
"they cannot be read in the constructor or any function or modifier called from it."
|
||||
@@ -205,6 +212,7 @@ void ImmutableValidator::checkAllVariablesInitialized(solidity::langutil::Source
|
||||
if (varDecl->immutable())
|
||||
if (!util::contains(m_initializedStateVariables, varDecl))
|
||||
m_errorReporter.typeError(
|
||||
2658_error,
|
||||
_location,
|
||||
solidity::langutil::SecondarySourceLocation().append("Not initialized: ", varDecl->location()),
|
||||
"Construction control flow ends without initializing all immutable state variables."
|
||||
|
||||
@@ -80,6 +80,7 @@ bool NameAndTypeResolver::performImports(SourceUnit& _sourceUnit, map<string, So
|
||||
if (!_sourceUnits.count(path))
|
||||
{
|
||||
m_errorReporter.declarationError(
|
||||
5073_error,
|
||||
imp->location(),
|
||||
"Import \"" + path + "\" (referenced as \"" + imp->path() + "\") not found."
|
||||
);
|
||||
@@ -95,6 +96,7 @@ bool NameAndTypeResolver::performImports(SourceUnit& _sourceUnit, map<string, So
|
||||
if (declarations.empty())
|
||||
{
|
||||
m_errorReporter.declarationError(
|
||||
2904_error,
|
||||
imp->location(),
|
||||
"Declaration \"" +
|
||||
alias.symbol->name() +
|
||||
@@ -208,6 +210,7 @@ void NameAndTypeResolver::warnVariablesNamedLikeInstructions()
|
||||
// Don't warn the user for what the user did not.
|
||||
continue;
|
||||
m_errorReporter.warning(
|
||||
8261_error,
|
||||
declaration->location(),
|
||||
"Variable is shadowed in inline assembly by an instruction of the same name"
|
||||
);
|
||||
@@ -326,6 +329,7 @@ void NameAndTypeResolver::importInheritedScope(ContractDefinition const& _base)
|
||||
}
|
||||
|
||||
m_errorReporter.declarationError(
|
||||
9097_error,
|
||||
secondDeclarationLocation,
|
||||
SecondarySourceLocation().append("The previous declaration is here:", firstDeclarationLocation),
|
||||
"Identifier already declared."
|
||||
@@ -343,19 +347,19 @@ void NameAndTypeResolver::linearizeBaseContracts(ContractDefinition& _contract)
|
||||
UserDefinedTypeName const& baseName = baseSpecifier->name();
|
||||
auto base = dynamic_cast<ContractDefinition const*>(baseName.annotation().referencedDeclaration);
|
||||
if (!base)
|
||||
m_errorReporter.fatalTypeError(baseName.location(), "Contract expected.");
|
||||
m_errorReporter.fatalTypeError(8758_error, baseName.location(), "Contract expected.");
|
||||
// "push_front" has the effect that bases mentioned later can overwrite members of bases
|
||||
// mentioned earlier
|
||||
input.back().push_front(base);
|
||||
vector<ContractDefinition const*> const& basesBases = base->annotation().linearizedBaseContracts;
|
||||
if (basesBases.empty())
|
||||
m_errorReporter.fatalTypeError(baseName.location(), "Definition of base has to precede definition of derived contract");
|
||||
m_errorReporter.fatalTypeError(2449_error, baseName.location(), "Definition of base has to precede definition of derived contract");
|
||||
input.push_front(list<ContractDefinition const*>(basesBases.begin(), basesBases.end()));
|
||||
}
|
||||
input.back().push_front(&_contract);
|
||||
vector<ContractDefinition const*> result = cThreeMerge(input);
|
||||
if (result.empty())
|
||||
m_errorReporter.fatalTypeError(_contract.location(), "Linearization of inheritance graph impossible");
|
||||
m_errorReporter.fatalTypeError(5005_error, _contract.location(), "Linearization of inheritance graph impossible");
|
||||
_contract.annotation().linearizedBaseContracts = result;
|
||||
_contract.annotation().contractDependencies.insert(result.begin() + 1, result.end());
|
||||
}
|
||||
@@ -476,6 +480,7 @@ bool DeclarationRegistrationHelper::registerDeclaration(
|
||||
}
|
||||
|
||||
_errorReporter.declarationError(
|
||||
2333_error,
|
||||
secondDeclarationLocation,
|
||||
SecondarySourceLocation().append("The previous declaration is here:", firstDeclarationLocation),
|
||||
"Identifier already declared."
|
||||
@@ -486,6 +491,7 @@ bool DeclarationRegistrationHelper::registerDeclaration(
|
||||
{
|
||||
if (dynamic_cast<MagicVariableDeclaration const*>(shadowedDeclaration))
|
||||
_errorReporter.warning(
|
||||
2319_error,
|
||||
*_errorLocation,
|
||||
"This declaration shadows a builtin symbol."
|
||||
);
|
||||
@@ -493,6 +499,7 @@ bool DeclarationRegistrationHelper::registerDeclaration(
|
||||
{
|
||||
auto shadowedLocation = shadowedDeclaration->location();
|
||||
_errorReporter.warning(
|
||||
2519_error,
|
||||
_declaration.location(),
|
||||
"This declaration shadows an existing declaration.",
|
||||
SecondarySourceLocation().append("The shadowed declaration is here:", shadowedLocation)
|
||||
|
||||
@@ -27,7 +27,6 @@
|
||||
#include <liblangutil/ErrorReporter.h>
|
||||
#include <libsolutil/Visitor.h>
|
||||
|
||||
#include <boost/range/adaptor/reversed.hpp>
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
|
||||
|
||||
@@ -461,6 +460,7 @@ void OverrideChecker::checkIllegalOverrides(ContractDefinition const& _contract)
|
||||
{
|
||||
if (contains_if(inheritedFuncs, MatchByName{modifier->name()}))
|
||||
m_errorReporter.typeError(
|
||||
5631_error,
|
||||
modifier->location(),
|
||||
"Override changes function or public state variable to modifier."
|
||||
);
|
||||
@@ -474,7 +474,7 @@ void OverrideChecker::checkIllegalOverrides(ContractDefinition const& _contract)
|
||||
continue;
|
||||
|
||||
if (contains_if(inheritedMods, MatchByName{function->name()}))
|
||||
m_errorReporter.typeError(function->location(), "Override changes modifier to function.");
|
||||
m_errorReporter.typeError(1469_error, function->location(), "Override changes modifier to function.");
|
||||
|
||||
checkOverrideList(OverrideProxy{function}, inheritedFuncs);
|
||||
}
|
||||
@@ -484,7 +484,7 @@ void OverrideChecker::checkIllegalOverrides(ContractDefinition const& _contract)
|
||||
continue;
|
||||
|
||||
if (contains_if(inheritedMods, MatchByName{stateVar->name()}))
|
||||
m_errorReporter.typeError(stateVar->location(), "Override changes modifier to public state variable.");
|
||||
m_errorReporter.typeError(1456_error, stateVar->location(), "Override changes modifier to public state variable.");
|
||||
|
||||
checkOverrideList(OverrideProxy{stateVar}, inheritedFuncs);
|
||||
}
|
||||
@@ -500,17 +500,19 @@ void OverrideChecker::checkOverride(OverrideProxy const& _overriding, OverridePr
|
||||
|
||||
if (_overriding.isModifier() && *_overriding.modifierType() != *_super.modifierType())
|
||||
m_errorReporter.typeError(
|
||||
1078_error,
|
||||
_overriding.location(),
|
||||
"Override changes modifier signature."
|
||||
);
|
||||
|
||||
if (!_overriding.overrides())
|
||||
overrideError(_overriding, _super, "Overriding " + _overriding.astNodeName() + " is missing \"override\" specifier.");
|
||||
overrideError(_overriding, _super, 9456_error, "Overriding " + _overriding.astNodeName() + " is missing \"override\" specifier.");
|
||||
|
||||
if (_super.isVariable())
|
||||
overrideError(
|
||||
_super,
|
||||
_overriding,
|
||||
1452_error,
|
||||
"Cannot override public state variable.",
|
||||
"Overriding " + _overriding.astNodeName() + " is here:"
|
||||
);
|
||||
@@ -518,6 +520,7 @@ void OverrideChecker::checkOverride(OverrideProxy const& _overriding, OverridePr
|
||||
overrideError(
|
||||
_super,
|
||||
_overriding,
|
||||
4334_error,
|
||||
"Trying to override non-virtual " + _super.astNodeName() + ". Did you forget to add \"virtual\"?",
|
||||
"Overriding " + _overriding.astNodeName() + " is here:"
|
||||
);
|
||||
@@ -525,7 +528,7 @@ void OverrideChecker::checkOverride(OverrideProxy const& _overriding, OverridePr
|
||||
if (_overriding.isVariable())
|
||||
{
|
||||
if (_super.visibility() != Visibility::External)
|
||||
overrideError(_overriding, _super, "Public state variables can only override functions with external visibility.");
|
||||
overrideError(_overriding, _super, 5225_error, "Public state variables can only override functions with external visibility.");
|
||||
solAssert(_overriding.visibility() == Visibility::External, "");
|
||||
}
|
||||
else if (_overriding.visibility() != _super.visibility())
|
||||
@@ -536,7 +539,7 @@ void OverrideChecker::checkOverride(OverrideProxy const& _overriding, OverridePr
|
||||
_super.visibility() == Visibility::External &&
|
||||
_overriding.visibility() == Visibility::Public
|
||||
))
|
||||
overrideError(_overriding, _super, "Overriding " + _overriding.astNodeName() + " visibility differs.");
|
||||
overrideError(_overriding, _super, 9098_error, "Overriding " + _overriding.astNodeName() + " visibility differs.");
|
||||
}
|
||||
|
||||
if (_super.isFunction())
|
||||
@@ -547,7 +550,7 @@ void OverrideChecker::checkOverride(OverrideProxy const& _overriding, OverridePr
|
||||
solAssert(functionType->hasEqualParameterTypes(*superType), "Override doesn't have equal parameters!");
|
||||
|
||||
if (!functionType->hasEqualReturnTypes(*superType))
|
||||
overrideError(_overriding, _super, "Overriding " + _overriding.astNodeName() + " return types differ.");
|
||||
overrideError(_overriding, _super, 4822_error, "Overriding " + _overriding.astNodeName() + " return types differ.");
|
||||
|
||||
// This is only relevant for a function overriding a function.
|
||||
if (_overriding.isFunction())
|
||||
@@ -556,6 +559,7 @@ void OverrideChecker::checkOverride(OverrideProxy const& _overriding, OverridePr
|
||||
overrideError(
|
||||
_overriding,
|
||||
_super,
|
||||
2837_error,
|
||||
"Overriding function changes state mutability from \"" +
|
||||
stateMutabilityToString(_super.stateMutability()) +
|
||||
"\" to \"" +
|
||||
@@ -567,6 +571,7 @@ void OverrideChecker::checkOverride(OverrideProxy const& _overriding, OverridePr
|
||||
overrideError(
|
||||
_overriding,
|
||||
_super,
|
||||
4593_error,
|
||||
"Overriding an implemented function with an unimplemented function is not allowed."
|
||||
);
|
||||
}
|
||||
@@ -576,6 +581,7 @@ void OverrideChecker::checkOverride(OverrideProxy const& _overriding, OverridePr
|
||||
void OverrideChecker::overrideListError(
|
||||
OverrideProxy const& _item,
|
||||
set<ContractDefinition const*, CompareByID> _secondary,
|
||||
ErrorId _error,
|
||||
string const& _message1,
|
||||
string const& _message2
|
||||
)
|
||||
@@ -593,6 +599,7 @@ void OverrideChecker::overrideListError(
|
||||
contractSingularPlural = "contracts ";
|
||||
|
||||
m_errorReporter.typeError(
|
||||
_error,
|
||||
_item.overrides() ? _item.overrides()->location() : _item.location(),
|
||||
ssl,
|
||||
_message1 +
|
||||
@@ -603,9 +610,10 @@ void OverrideChecker::overrideListError(
|
||||
);
|
||||
}
|
||||
|
||||
void OverrideChecker::overrideError(Declaration const& _overriding, Declaration const& _super, string const& _message, string const& _secondaryMsg)
|
||||
void OverrideChecker::overrideError(Declaration const& _overriding, Declaration const& _super, ErrorId _error, string const& _message, string const& _secondaryMsg)
|
||||
{
|
||||
m_errorReporter.typeError(
|
||||
_error,
|
||||
_overriding.location(),
|
||||
SecondarySourceLocation().append(_secondaryMsg, _super.location()),
|
||||
_message
|
||||
@@ -613,9 +621,10 @@ void OverrideChecker::overrideError(Declaration const& _overriding, Declaration
|
||||
}
|
||||
|
||||
|
||||
void OverrideChecker::overrideError(OverrideProxy const& _overriding, OverrideProxy const& _super, string const& _message, string const& _secondaryMsg)
|
||||
void OverrideChecker::overrideError(OverrideProxy const& _overriding, OverrideProxy const& _super, ErrorId _error, string const& _message, string const& _secondaryMsg)
|
||||
{
|
||||
m_errorReporter.typeError(
|
||||
_error,
|
||||
_overriding.location(),
|
||||
SecondarySourceLocation().append(_secondaryMsg, _super.location()),
|
||||
_message
|
||||
@@ -718,7 +727,7 @@ void OverrideChecker::checkAmbiguousOverridesInternal(set<OverrideProxy> _baseCa
|
||||
" Since one of the bases defines a public state variable which cannot be overridden, "
|
||||
"you have to change the inheritance layout or the names of the functions.";
|
||||
|
||||
m_errorReporter.typeError(_location, ssl, message);
|
||||
m_errorReporter.typeError(6480_error, _location, ssl, message);
|
||||
}
|
||||
|
||||
set<ContractDefinition const*, OverrideChecker::CompareByID> OverrideChecker::resolveOverrideList(OverrideSpecifier const& _overrides) const
|
||||
@@ -766,13 +775,14 @@ void OverrideChecker::checkOverrideList(OverrideProxy _item, OverrideProxyBySign
|
||||
SecondarySourceLocation ssl;
|
||||
ssl.append("First occurrence here: ", list[i-1]->location());
|
||||
m_errorReporter.typeError(
|
||||
4520_error,
|
||||
list[i]->location(),
|
||||
ssl,
|
||||
"Duplicate contract \"" +
|
||||
joinHumanReadable(list[i]->namePath(), ".") +
|
||||
"\" found in override list of \"" +
|
||||
_item.name() +
|
||||
"\"."
|
||||
"Duplicate contract \"" +
|
||||
joinHumanReadable(list[i]->namePath(), ".") +
|
||||
"\" found in override list of \"" +
|
||||
_item.name() +
|
||||
"\"."
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -791,6 +801,7 @@ void OverrideChecker::checkOverrideList(OverrideProxy _item, OverrideProxyBySign
|
||||
|
||||
if (_item.overrides() && expectedContracts.empty())
|
||||
m_errorReporter.typeError(
|
||||
7792_error,
|
||||
_item.overrides()->location(),
|
||||
_item.astNodeNameCapitalized() + " has override specified but does not override anything."
|
||||
);
|
||||
@@ -804,6 +815,7 @@ void OverrideChecker::checkOverrideList(OverrideProxy _item, OverrideProxyBySign
|
||||
overrideListError(
|
||||
_item,
|
||||
missingContracts,
|
||||
4327_error,
|
||||
_item.astNodeNameCapitalized() + " needs to specify overridden ",
|
||||
""
|
||||
);
|
||||
@@ -813,6 +825,7 @@ void OverrideChecker::checkOverrideList(OverrideProxy _item, OverrideProxyBySign
|
||||
overrideListError(
|
||||
_item,
|
||||
surplusContracts,
|
||||
2353_error,
|
||||
"Invalid ",
|
||||
"specified in override list: "
|
||||
);
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
namespace solidity::langutil
|
||||
{
|
||||
class ErrorReporter;
|
||||
struct ErrorId;
|
||||
struct SourceLocation;
|
||||
}
|
||||
|
||||
@@ -160,18 +161,21 @@ private:
|
||||
void overrideListError(
|
||||
OverrideProxy const& _item,
|
||||
std::set<ContractDefinition const*, CompareByID> _secondary,
|
||||
langutil::ErrorId _error,
|
||||
std::string const& _message1,
|
||||
std::string const& _message2
|
||||
);
|
||||
void overrideError(
|
||||
Declaration const& _overriding,
|
||||
Declaration const& _super,
|
||||
langutil::ErrorId _error,
|
||||
std::string const& _message,
|
||||
std::string const& _secondaryMsg = "Overridden function is here:"
|
||||
);
|
||||
void overrideError(
|
||||
OverrideProxy const& _overriding,
|
||||
OverrideProxy const& _super,
|
||||
langutil::ErrorId _error,
|
||||
std::string const& _message,
|
||||
std::string const& _secondaryMsg = "Overridden function is here:"
|
||||
);
|
||||
|
||||
@@ -122,6 +122,7 @@ struct ConstStateVarCircularReferenceChecker: public PostTypeChecker::Checker
|
||||
for (auto declaration: m_constVariables)
|
||||
if (auto identifier = findCycle(*declaration))
|
||||
m_errorReporter.typeError(
|
||||
6161_error,
|
||||
declaration->location(),
|
||||
"The value of the constant " + declaration->name() +
|
||||
" has a cyclic dependency via " + identifier->name() + "."
|
||||
@@ -165,7 +166,7 @@ struct ConstStateVarCircularReferenceChecker: public PostTypeChecker::Checker
|
||||
auto visitor = [&](VariableDeclaration const& _variable, util::CycleDetector<VariableDeclaration>& _cycleDetector, size_t _depth)
|
||||
{
|
||||
if (_depth >= 256)
|
||||
m_errorReporter.fatalDeclarationError(_variable.location(), "Variable definition exhausting cyclic dependency validator.");
|
||||
m_errorReporter.fatalDeclarationError(7380_error, _variable.location(), "Variable definition exhausting cyclic dependency validator.");
|
||||
|
||||
// Iterating through the dependencies needs to be deterministic and thus cannot
|
||||
// depend on the memory layout.
|
||||
@@ -209,6 +210,7 @@ struct OverrideSpecifierChecker: public PostTypeChecker::Checker
|
||||
TypeType const* actualTypeType = dynamic_cast<TypeType const*>(decl->type());
|
||||
|
||||
m_errorReporter.typeError(
|
||||
9301_error,
|
||||
override->location(),
|
||||
"Expected contract but got " +
|
||||
actualTypeType->actualType()->toString(true) +
|
||||
@@ -243,6 +245,7 @@ struct ModifierContextChecker: public PostTypeChecker::Checker
|
||||
if (ModifierType const* type = dynamic_cast<decltype(type)>(_identifier.annotation().type))
|
||||
{
|
||||
m_errorReporter.typeError(
|
||||
3112_error,
|
||||
_identifier.location(),
|
||||
"Modifier can only be referenced in function headers."
|
||||
);
|
||||
@@ -280,6 +283,7 @@ struct EventOutsideEmitChecker: public PostTypeChecker::Checker
|
||||
// Check for event outside of emit statement
|
||||
if (!m_insideEmitStatement && functionType->kind() == FunctionType::Kind::Event)
|
||||
m_errorReporter.typeError(
|
||||
3132_error,
|
||||
_functionCall.location(),
|
||||
"Event invocations have to be prefixed by \"emit\"."
|
||||
);
|
||||
@@ -308,7 +312,7 @@ struct NoVariablesInInterfaceChecker: public PostTypeChecker::Checker
|
||||
&& !_variable.isCallableOrCatchParameter()
|
||||
&& !m_insideStruct
|
||||
)
|
||||
m_errorReporter.typeError(_variable.location(), "Variables cannot be declared in interfaces.");
|
||||
m_errorReporter.typeError(8274_error, _variable.location(), "Variables cannot be declared in interfaces.");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -268,19 +268,19 @@ void ReferencesResolver::operator()(yul::VariableDeclaration const& _varDecl)
|
||||
void ReferencesResolver::declarationError(SourceLocation const& _location, string const& _description)
|
||||
{
|
||||
m_errorOccurred = true;
|
||||
m_errorReporter.declarationError(_location, _description);
|
||||
m_errorReporter.declarationError(8532_error, _location, _description);
|
||||
}
|
||||
|
||||
void ReferencesResolver::declarationError(SourceLocation const& _location, SecondarySourceLocation const& _ssl, string const& _description)
|
||||
{
|
||||
m_errorOccurred = true;
|
||||
m_errorReporter.declarationError(_location, _ssl, _description);
|
||||
m_errorReporter.declarationError(3881_error, _location, _ssl, _description);
|
||||
}
|
||||
|
||||
void ReferencesResolver::fatalDeclarationError(SourceLocation const& _location, string const& _description)
|
||||
{
|
||||
m_errorOccurred = true;
|
||||
m_errorReporter.fatalDeclarationError(_location, _description);
|
||||
m_errorReporter.fatalDeclarationError(6546_error, _location, _description);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -120,13 +120,14 @@ void StaticAnalyzer::endVisit(FunctionDefinition const&)
|
||||
{
|
||||
if (var.first.second->isCallableOrCatchParameter())
|
||||
m_errorReporter.warning(
|
||||
5667_error,
|
||||
var.first.second->location(),
|
||||
"Unused " +
|
||||
string(var.first.second->isTryCatchParameter() ? "try/catch" : "function") +
|
||||
" parameter. Remove or comment out the variable name to silence this warning."
|
||||
);
|
||||
else
|
||||
m_errorReporter.warning(var.first.second->location(), "Unused local variable.");
|
||||
m_errorReporter.warning(2072_error, var.first.second->location(), "Unused local variable.");
|
||||
}
|
||||
m_localVarUseCount.clear();
|
||||
m_constructor = false;
|
||||
@@ -159,6 +160,7 @@ bool StaticAnalyzer::visit(VariableDeclaration const& _variable)
|
||||
set<StructDefinition const*> structsSeen;
|
||||
if (structureSizeEstimate(*_variable.type(), structsSeen) >= bigint(1) << 64)
|
||||
m_errorReporter.warning(
|
||||
3408_error,
|
||||
_variable.location(),
|
||||
"Variable covers a large part of storage and thus makes collisions likely. "
|
||||
"Either use mappings or dynamic arrays and allow their size to be increased only "
|
||||
@@ -183,6 +185,7 @@ bool StaticAnalyzer::visit(ExpressionStatement const& _statement)
|
||||
{
|
||||
if (_statement.expression().annotation().isPure)
|
||||
m_errorReporter.warning(
|
||||
6133_error,
|
||||
_statement.location(),
|
||||
"Statement has no effect."
|
||||
);
|
||||
@@ -196,11 +199,13 @@ bool StaticAnalyzer::visit(MemberAccess const& _memberAccess)
|
||||
{
|
||||
if (type->kind() == MagicType::Kind::Message && _memberAccess.memberName() == "gas")
|
||||
m_errorReporter.typeError(
|
||||
1400_error,
|
||||
_memberAccess.location(),
|
||||
"\"msg.gas\" has been deprecated in favor of \"gasleft()\""
|
||||
);
|
||||
else if (type->kind() == MagicType::Kind::Block && _memberAccess.memberName() == "blockhash")
|
||||
m_errorReporter.typeError(
|
||||
8113_error,
|
||||
_memberAccess.location(),
|
||||
"\"block.blockhash()\" has been deprecated in favor of \"blockhash()\""
|
||||
);
|
||||
@@ -211,6 +216,7 @@ bool StaticAnalyzer::visit(MemberAccess const& _memberAccess)
|
||||
ContractType const& contract = dynamic_cast<ContractType const&>(*type->typeArgument());
|
||||
if (m_constructorUsesAssembly->check(contract.contractDefinition()))
|
||||
m_errorReporter.warning(
|
||||
6417_error,
|
||||
_memberAccess.location(),
|
||||
"The constructor of the contract (or its base) uses inline assembly. "
|
||||
"Because of that, it might be that the deployed bytecode is different from type(...).runtimeCode."
|
||||
@@ -222,6 +228,7 @@ bool StaticAnalyzer::visit(MemberAccess const& _memberAccess)
|
||||
if (auto const* type = dynamic_cast<FunctionType const*>(_memberAccess.annotation().type))
|
||||
if (type->kind() == FunctionType::Kind::BareCallCode)
|
||||
m_errorReporter.typeError(
|
||||
2256_error,
|
||||
_memberAccess.location(),
|
||||
"\"callcode\" has been deprecated in favour of \"delegatecall\"."
|
||||
);
|
||||
@@ -235,6 +242,7 @@ bool StaticAnalyzer::visit(MemberAccess const& _memberAccess)
|
||||
{
|
||||
if (id->name() == "this")
|
||||
m_errorReporter.warning(
|
||||
5805_error,
|
||||
id->location(),
|
||||
"\"this\" used in constructor. "
|
||||
"Note that external functions of a contract "
|
||||
@@ -285,6 +293,7 @@ bool StaticAnalyzer::visit(BinaryOperation const& _operation)
|
||||
))
|
||||
if (rhs->isZero())
|
||||
m_errorReporter.typeError(
|
||||
1211_error,
|
||||
_operation.location(),
|
||||
(_operation.getOperator() == Token::Div) ? "Division by zero." : "Modulo zero."
|
||||
);
|
||||
@@ -307,6 +316,7 @@ bool StaticAnalyzer::visit(FunctionCall const& _functionCall)
|
||||
))
|
||||
if (lastArg->isZero())
|
||||
m_errorReporter.typeError(
|
||||
4195_error,
|
||||
_functionCall.location(),
|
||||
"Arithmetic modulo zero."
|
||||
);
|
||||
@@ -317,6 +327,7 @@ bool StaticAnalyzer::visit(FunctionCall const& _functionCall)
|
||||
functionType->declaration().scope() == m_currentContract
|
||||
)
|
||||
m_errorReporter.typeError(
|
||||
6700_error,
|
||||
_functionCall.location(),
|
||||
SecondarySourceLocation().append(
|
||||
"The function declaration is here:",
|
||||
|
||||
@@ -27,7 +27,6 @@
|
||||
#include <liblangutil/ErrorReporter.h>
|
||||
#include <liblangutil/SemVerHandler.h>
|
||||
|
||||
#include <boost/algorithm/cxx11/all_of.hpp>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
||||
#include <memory>
|
||||
@@ -69,7 +68,7 @@ void SyntaxChecker::endVisit(SourceUnit const& _sourceUnit)
|
||||
string(";\"");
|
||||
|
||||
// when reporting the warning, print the source name only
|
||||
m_errorReporter.warning({-1, -1, _sourceUnit.location().source}, errorString);
|
||||
m_errorReporter.warning(3420_error, {-1, -1, _sourceUnit.location().source}, errorString);
|
||||
}
|
||||
m_sourceUnit = nullptr;
|
||||
}
|
||||
@@ -79,18 +78,20 @@ bool SyntaxChecker::visit(PragmaDirective const& _pragma)
|
||||
solAssert(!_pragma.tokens().empty(), "");
|
||||
solAssert(_pragma.tokens().size() == _pragma.literals().size(), "");
|
||||
if (_pragma.tokens()[0] != Token::Identifier)
|
||||
m_errorReporter.syntaxError(_pragma.location(), "Invalid pragma \"" + _pragma.literals()[0] + "\"");
|
||||
m_errorReporter.syntaxError(5226_error, _pragma.location(), "Invalid pragma \"" + _pragma.literals()[0] + "\"");
|
||||
else if (_pragma.literals()[0] == "experimental")
|
||||
{
|
||||
solAssert(m_sourceUnit, "");
|
||||
vector<string> literals(_pragma.literals().begin() + 1, _pragma.literals().end());
|
||||
if (literals.empty())
|
||||
m_errorReporter.syntaxError(
|
||||
9679_error,
|
||||
_pragma.location(),
|
||||
"Experimental feature name is missing."
|
||||
);
|
||||
else if (literals.size() > 1)
|
||||
m_errorReporter.syntaxError(
|
||||
6022_error,
|
||||
_pragma.location(),
|
||||
"Stray arguments."
|
||||
);
|
||||
@@ -98,17 +99,17 @@ bool SyntaxChecker::visit(PragmaDirective const& _pragma)
|
||||
{
|
||||
string const literal = literals[0];
|
||||
if (literal.empty())
|
||||
m_errorReporter.syntaxError(_pragma.location(), "Empty experimental feature name is invalid.");
|
||||
m_errorReporter.syntaxError(3250_error, _pragma.location(), "Empty experimental feature name is invalid.");
|
||||
else if (!ExperimentalFeatureNames.count(literal))
|
||||
m_errorReporter.syntaxError(_pragma.location(), "Unsupported experimental feature name.");
|
||||
m_errorReporter.syntaxError(8491_error, _pragma.location(), "Unsupported experimental feature name.");
|
||||
else if (m_sourceUnit->annotation().experimentalFeatures.count(ExperimentalFeatureNames.at(literal)))
|
||||
m_errorReporter.syntaxError(_pragma.location(), "Duplicate experimental feature name.");
|
||||
m_errorReporter.syntaxError(1231_error, _pragma.location(), "Duplicate experimental feature name.");
|
||||
else
|
||||
{
|
||||
auto feature = ExperimentalFeatureNames.at(literal);
|
||||
m_sourceUnit->annotation().experimentalFeatures.insert(feature);
|
||||
if (!ExperimentalFeatureWithoutWarning.count(feature))
|
||||
m_errorReporter.warning(_pragma.location(), "Experimental features are turned on. Do not use experimental features on live deployments.");
|
||||
m_errorReporter.warning(2264_error, _pragma.location(), "Experimental features are turned on. Do not use experimental features on live deployments.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -121,15 +122,16 @@ bool SyntaxChecker::visit(PragmaDirective const& _pragma)
|
||||
static SemVerVersion const currentVersion{string(VersionString)};
|
||||
if (!matchExpression.matches(currentVersion))
|
||||
m_errorReporter.syntaxError(
|
||||
3997_error,
|
||||
_pragma.location(),
|
||||
"Source file requires different compiler version (current compiler is " +
|
||||
string(VersionString) + " - note that nightly builds are considered to be "
|
||||
string(VersionString) + ") - note that nightly builds are considered to be "
|
||||
"strictly less than the released version"
|
||||
);
|
||||
m_versionPragmaFound = true;
|
||||
}
|
||||
else
|
||||
m_errorReporter.syntaxError(_pragma.location(), "Unknown pragma \"" + _pragma.literals()[0] + "\"");
|
||||
m_errorReporter.syntaxError(4936_error, _pragma.location(), "Unknown pragma \"" + _pragma.literals()[0] + "\"");
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -142,7 +144,7 @@ bool SyntaxChecker::visit(ModifierDefinition const&)
|
||||
void SyntaxChecker::endVisit(ModifierDefinition const& _modifier)
|
||||
{
|
||||
if (_modifier.isImplemented() && !m_placeholderFound)
|
||||
m_errorReporter.syntaxError(_modifier.body().location(), "Modifier body does not contain '_'.");
|
||||
m_errorReporter.syntaxError(2883_error, _modifier.body().location(), "Modifier body does not contain '_'.");
|
||||
m_placeholderFound = false;
|
||||
}
|
||||
|
||||
@@ -150,7 +152,7 @@ void SyntaxChecker::checkSingleStatementVariableDeclaration(ASTNode const& _stat
|
||||
{
|
||||
auto varDecl = dynamic_cast<VariableDeclarationStatement const*>(&_statement);
|
||||
if (varDecl)
|
||||
m_errorReporter.syntaxError(_statement.location(), "Variable declarations can only be used inside blocks.");
|
||||
m_errorReporter.syntaxError(9079_error, _statement.location(), "Variable declarations can only be used inside blocks.");
|
||||
}
|
||||
|
||||
bool SyntaxChecker::visit(IfStatement const& _ifStatement)
|
||||
@@ -189,7 +191,7 @@ bool SyntaxChecker::visit(Continue const& _continueStatement)
|
||||
{
|
||||
if (m_inLoopDepth <= 0)
|
||||
// we're not in a for/while loop, report syntax error
|
||||
m_errorReporter.syntaxError(_continueStatement.location(), "\"continue\" has to be in a \"for\" or \"while\" loop.");
|
||||
m_errorReporter.syntaxError(4123_error, _continueStatement.location(), "\"continue\" has to be in a \"for\" or \"while\" loop.");
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -197,13 +199,14 @@ bool SyntaxChecker::visit(Break const& _breakStatement)
|
||||
{
|
||||
if (m_inLoopDepth <= 0)
|
||||
// we're not in a for/while loop, report syntax error
|
||||
m_errorReporter.syntaxError(_breakStatement.location(), "\"break\" has to be in a \"for\" or \"while\" loop.");
|
||||
m_errorReporter.syntaxError(6102_error, _breakStatement.location(), "\"break\" has to be in a \"for\" or \"while\" loop.");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SyntaxChecker::visit(Throw const& _throwStatement)
|
||||
{
|
||||
m_errorReporter.syntaxError(
|
||||
4538_error,
|
||||
_throwStatement.location(),
|
||||
"\"throw\" is deprecated in favour of \"revert()\", \"require()\" and \"assert()\"."
|
||||
);
|
||||
@@ -222,29 +225,29 @@ bool SyntaxChecker::visit(Literal const& _literal)
|
||||
// Generic checks no matter what base this number literal is of:
|
||||
if (value.back() == '_')
|
||||
{
|
||||
m_errorReporter.syntaxError(_literal.location(), "Invalid use of underscores in number literal. No trailing underscores allowed.");
|
||||
m_errorReporter.syntaxError(2090_error, _literal.location(), "Invalid use of underscores in number literal. No trailing underscores allowed.");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (value.find("__") != ASTString::npos)
|
||||
{
|
||||
m_errorReporter.syntaxError(_literal.location(), "Invalid use of underscores in number literal. Only one consecutive underscores between digits allowed.");
|
||||
m_errorReporter.syntaxError(2990_error, _literal.location(), "Invalid use of underscores in number literal. Only one consecutive underscores between digits allowed.");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!_literal.isHexNumber()) // decimal literal
|
||||
{
|
||||
if (value.find("._") != ASTString::npos)
|
||||
m_errorReporter.syntaxError(_literal.location(), "Invalid use of underscores in number literal. No underscores in front of the fraction part allowed.");
|
||||
m_errorReporter.syntaxError(3891_error, _literal.location(), "Invalid use of underscores in number literal. No underscores in front of the fraction part allowed.");
|
||||
|
||||
if (value.find("_.") != ASTString::npos)
|
||||
m_errorReporter.syntaxError(_literal.location(), "Invalid use of underscores in number literal. No underscores in front of the fraction part allowed.");
|
||||
m_errorReporter.syntaxError(1023_error, _literal.location(), "Invalid use of underscores in number literal. No underscores in front of the fraction part allowed.");
|
||||
|
||||
if (value.find("_e") != ASTString::npos)
|
||||
m_errorReporter.syntaxError(_literal.location(), "Invalid use of underscores in number literal. No underscore at the end of the mantissa allowed.");
|
||||
m_errorReporter.syntaxError(6415_error, _literal.location(), "Invalid use of underscores in number literal. No underscore at the end of the mantissa allowed.");
|
||||
|
||||
if (value.find("e_") != ASTString::npos)
|
||||
m_errorReporter.syntaxError(_literal.location(), "Invalid use of underscores in number literal. No underscore in front of exponent allowed.");
|
||||
m_errorReporter.syntaxError(6165_error, _literal.location(), "Invalid use of underscores in number literal. No underscore in front of exponent allowed.");
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -253,7 +256,7 @@ bool SyntaxChecker::visit(Literal const& _literal)
|
||||
bool SyntaxChecker::visit(UnaryOperation const& _operation)
|
||||
{
|
||||
if (_operation.getOperator() == Token::Add)
|
||||
m_errorReporter.syntaxError(_operation.location(), "Use of unary + is disallowed.");
|
||||
m_errorReporter.syntaxError(9636_error, _operation.location(), "Use of unary + is disallowed.");
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -265,6 +268,7 @@ bool SyntaxChecker::visit(InlineAssembly const& _inlineAssembly)
|
||||
|
||||
if (yul::MSizeFinder::containsMSize(_inlineAssembly.dialect(), _inlineAssembly.operations()))
|
||||
m_errorReporter.syntaxError(
|
||||
6553_error,
|
||||
_inlineAssembly.location(),
|
||||
"The msize instruction cannot be used when the Yul optimizer is activated because "
|
||||
"it can change its semantics. Either disable the Yul optimizer or do not use the instruction."
|
||||
@@ -285,7 +289,9 @@ bool SyntaxChecker::visit(ContractDefinition const& _contract)
|
||||
ASTString const& contractName = _contract.name();
|
||||
for (FunctionDefinition const* function: _contract.definedFunctions())
|
||||
if (function->name() == contractName)
|
||||
m_errorReporter.syntaxError(function->location(),
|
||||
m_errorReporter.syntaxError(
|
||||
5796_error,
|
||||
function->location(),
|
||||
"Functions are not allowed to have the same name as the contract. "
|
||||
"If you intend this to be a constructor, use \"constructor(...) { ... }\" to define it."
|
||||
);
|
||||
@@ -298,15 +304,16 @@ bool SyntaxChecker::visit(FunctionDefinition const& _function)
|
||||
{
|
||||
string suggestedVisibility = _function.isFallback() || _function.isReceive() || m_isInterface ? "external" : "public";
|
||||
m_errorReporter.syntaxError(
|
||||
4937_error,
|
||||
_function.location(),
|
||||
"No visibility specified. Did you intend to add \"" + suggestedVisibility + "\"?"
|
||||
);
|
||||
}
|
||||
|
||||
if (m_isInterface && !_function.modifiers().empty())
|
||||
m_errorReporter.syntaxError(_function.location(), "Functions in interfaces cannot have modifiers.");
|
||||
m_errorReporter.syntaxError(5842_error, _function.location(), "Functions in interfaces cannot have modifiers.");
|
||||
else if (!_function.isImplemented() && !_function.modifiers().empty())
|
||||
m_errorReporter.syntaxError(_function.location(), "Functions without implementation cannot have modifiers.");
|
||||
m_errorReporter.syntaxError(2668_error, _function.location(), "Functions without implementation cannot have modifiers.");
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -315,11 +322,11 @@ bool SyntaxChecker::visit(FunctionTypeName const& _node)
|
||||
{
|
||||
for (auto const& decl: _node.parameterTypeList()->parameters())
|
||||
if (!decl->name().empty())
|
||||
m_errorReporter.warning(decl->location(), "Naming function type parameters is deprecated.");
|
||||
m_errorReporter.warning(6162_error, decl->location(), "Naming function type parameters is deprecated.");
|
||||
|
||||
for (auto const& decl: _node.returnParameterTypeList()->parameters())
|
||||
if (!decl->name().empty())
|
||||
m_errorReporter.syntaxError(decl->location(), "Return parameters in function types may not be named.");
|
||||
m_errorReporter.syntaxError(7304_error, decl->location(), "Return parameters in function types may not be named.");
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -327,8 +334,13 @@ bool SyntaxChecker::visit(FunctionTypeName const& _node)
|
||||
bool SyntaxChecker::visit(VariableDeclarationStatement const& _statement)
|
||||
{
|
||||
// Report if none of the variable components in the tuple have a name (only possible via deprecated "var")
|
||||
if (boost::algorithm::all_of_equal(_statement.declarations(), nullptr))
|
||||
if (std::all_of(
|
||||
_statement.declarations().begin(),
|
||||
_statement.declarations().end(),
|
||||
[](ASTPointer<VariableDeclaration> const& declaration) { return declaration == nullptr; }
|
||||
))
|
||||
m_errorReporter.syntaxError(
|
||||
3299_error,
|
||||
_statement.location(),
|
||||
"The use of the \"var\" keyword is disallowed. The declaration part of the statement can be removed, since it is empty."
|
||||
);
|
||||
@@ -339,7 +351,7 @@ bool SyntaxChecker::visit(VariableDeclarationStatement const& _statement)
|
||||
bool SyntaxChecker::visit(StructDefinition const& _struct)
|
||||
{
|
||||
if (_struct.members().empty())
|
||||
m_errorReporter.syntaxError(_struct.location(), "Defining empty structs is disallowed.");
|
||||
m_errorReporter.syntaxError(5306_error, _struct.location(), "Defining empty structs is disallowed.");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -171,6 +171,7 @@ void ViewPureChecker::endVisit(FunctionDefinition const& _funDef)
|
||||
!_funDef.overrides()
|
||||
)
|
||||
m_errorReporter.warning(
|
||||
2018_error,
|
||||
_funDef.location(),
|
||||
"Function state mutability can be restricted to " + stateMutabilityToString(m_bestMutabilityAndLocation.mutability)
|
||||
);
|
||||
@@ -229,7 +230,7 @@ void ViewPureChecker::endVisit(InlineAssembly const& _inlineAssembly)
|
||||
{
|
||||
AssemblyViewPureChecker{
|
||||
_inlineAssembly.dialect(),
|
||||
[=](StateMutability _mutability, SourceLocation const& _location) { reportMutability(_mutability, _location); }
|
||||
[&](StateMutability _mutability, SourceLocation const& _location) { reportMutability(_mutability, _location); }
|
||||
}(_inlineAssembly.operations());
|
||||
}
|
||||
|
||||
@@ -252,6 +253,7 @@ void ViewPureChecker::reportMutability(
|
||||
))
|
||||
{
|
||||
m_errorReporter.typeError(
|
||||
2527_error,
|
||||
_location,
|
||||
"Function declared as pure, but this expression (potentially) reads from the "
|
||||
"environment or state and thus requires \"view\"."
|
||||
@@ -261,6 +263,7 @@ void ViewPureChecker::reportMutability(
|
||||
else if (_mutability == StateMutability::NonPayable)
|
||||
{
|
||||
m_errorReporter.typeError(
|
||||
8961_error,
|
||||
_location,
|
||||
"Function declared as " +
|
||||
stateMutabilityToString(m_currentFunction->stateMutability()) +
|
||||
@@ -277,12 +280,14 @@ void ViewPureChecker::reportMutability(
|
||||
{
|
||||
if (_nestedLocation)
|
||||
m_errorReporter.typeError(
|
||||
4006_error,
|
||||
_location,
|
||||
SecondarySourceLocation().append("\"msg.value\" or \"callvalue()\" appear here inside the modifier.", *_nestedLocation),
|
||||
"This modifier uses \"msg.value\" or \"callvalue()\" and thus the function has to be payable or internal."
|
||||
);
|
||||
else
|
||||
m_errorReporter.typeError(
|
||||
5887_error,
|
||||
_location,
|
||||
"\"msg.value\" and \"callvalue()\" can only be used in payable public functions. Make the function "
|
||||
"\"payable\" or use an internal function to avoid this error."
|
||||
@@ -357,6 +362,8 @@ void ViewPureChecker::endVisit(MemberAccess const& _memberAccess)
|
||||
{MagicType::Kind::MetaType, "runtimeCode"},
|
||||
{MagicType::Kind::MetaType, "name"},
|
||||
{MagicType::Kind::MetaType, "interfaceId"},
|
||||
{MagicType::Kind::MetaType, "min"},
|
||||
{MagicType::Kind::MetaType, "max"},
|
||||
};
|
||||
set<MagicMember> static const payableMembers{
|
||||
{MagicType::Kind::Message, "value"}
|
||||
|
||||
@@ -556,7 +556,13 @@ MagicType const* TypeProvider::magic(MagicType::Kind _kind)
|
||||
|
||||
MagicType const* TypeProvider::meta(Type const* _type)
|
||||
{
|
||||
solAssert(_type && _type->category() == Type::Category::Contract, "Only contracts supported for now.");
|
||||
solAssert(
|
||||
_type && (
|
||||
_type->category() == Type::Category::Contract ||
|
||||
_type->category() == Type::Category::Integer
|
||||
),
|
||||
"Only contracts or integer types supported for now."
|
||||
);
|
||||
return createAndGet<MagicType>(_type);
|
||||
}
|
||||
|
||||
|
||||
+42
-11
@@ -556,6 +556,22 @@ string IntegerType::toString(bool) const
|
||||
return prefix + util::toString(m_bits);
|
||||
}
|
||||
|
||||
u256 IntegerType::min() const
|
||||
{
|
||||
if (isSigned())
|
||||
return s2u(s256(minValue()));
|
||||
else
|
||||
return u256(minValue());
|
||||
}
|
||||
|
||||
u256 IntegerType::max() const
|
||||
{
|
||||
if (isSigned())
|
||||
return s2u(s256(maxValue()));
|
||||
else
|
||||
return u256(maxValue());
|
||||
}
|
||||
|
||||
bigint IntegerType::minValue() const
|
||||
{
|
||||
if (isSigned())
|
||||
@@ -3763,20 +3779,35 @@ MemberList::MemberMap MagicType::nativeMembers(ContractDefinition const*) const
|
||||
case Kind::MetaType:
|
||||
{
|
||||
solAssert(
|
||||
m_typeArgument && m_typeArgument->category() == Type::Category::Contract,
|
||||
"Only contracts supported for now"
|
||||
m_typeArgument && (
|
||||
m_typeArgument->category() == Type::Category::Contract ||
|
||||
m_typeArgument->category() == Type::Category::Integer
|
||||
),
|
||||
"Only contracts or integer types supported for now"
|
||||
);
|
||||
ContractDefinition const& contract = dynamic_cast<ContractType const&>(*m_typeArgument).contractDefinition();
|
||||
if (contract.canBeDeployed())
|
||||
|
||||
if (m_typeArgument->category() == Type::Category::Contract)
|
||||
{
|
||||
ContractDefinition const& contract = dynamic_cast<ContractType const&>(*m_typeArgument).contractDefinition();
|
||||
if (contract.canBeDeployed())
|
||||
return MemberList::MemberMap({
|
||||
{"creationCode", TypeProvider::array(DataLocation::Memory)},
|
||||
{"runtimeCode", TypeProvider::array(DataLocation::Memory)},
|
||||
{"name", TypeProvider::stringMemory()},
|
||||
});
|
||||
else
|
||||
return MemberList::MemberMap({
|
||||
{"interfaceId", TypeProvider::fixedBytes(4)},
|
||||
});
|
||||
}
|
||||
else if (m_typeArgument->category() == Type::Category::Integer)
|
||||
{
|
||||
IntegerType const* integerTypePointer = dynamic_cast<IntegerType const*>(m_typeArgument);
|
||||
return MemberList::MemberMap({
|
||||
{"creationCode", TypeProvider::array(DataLocation::Memory)},
|
||||
{"runtimeCode", TypeProvider::array(DataLocation::Memory)},
|
||||
{"name", TypeProvider::stringMemory()},
|
||||
});
|
||||
else
|
||||
return MemberList::MemberMap({
|
||||
{"interfaceId", TypeProvider::fixedBytes(4)},
|
||||
{"min", integerTypePointer},
|
||||
{"max", integerTypePointer},
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
solAssert(false, "Unknown kind of magic.");
|
||||
|
||||
@@ -452,6 +452,9 @@ public:
|
||||
unsigned numBits() const { return m_bits; }
|
||||
bool isSigned() const { return m_modifier == Modifier::Signed; }
|
||||
|
||||
u256 min() const;
|
||||
u256 max() const;
|
||||
|
||||
bigint minValue() const;
|
||||
bigint maxValue() const;
|
||||
|
||||
|
||||
@@ -28,7 +28,6 @@
|
||||
#include <libsolutil/StringUtils.h>
|
||||
|
||||
#include <boost/algorithm/string/join.hpp>
|
||||
#include <boost/range/adaptor/reversed.hpp>
|
||||
|
||||
using namespace std;
|
||||
using namespace solidity;
|
||||
@@ -38,7 +37,8 @@ using namespace solidity::frontend;
|
||||
string ABIFunctions::tupleEncoder(
|
||||
TypePointers const& _givenTypes,
|
||||
TypePointers const& _targetTypes,
|
||||
bool _encodeAsLibraryTypes
|
||||
bool _encodeAsLibraryTypes,
|
||||
bool _reversed
|
||||
)
|
||||
{
|
||||
EncodingOptions options;
|
||||
@@ -54,6 +54,8 @@ string ABIFunctions::tupleEncoder(
|
||||
for (auto const& t: _targetTypes)
|
||||
functionName += t->identifier() + "_";
|
||||
functionName += options.toFunctionNameSuffix();
|
||||
if (_reversed)
|
||||
functionName += "_reversed";
|
||||
|
||||
return createFunction(functionName, [&]() {
|
||||
// Note that the values are in reverse due to the difference in calling semantics.
|
||||
@@ -94,7 +96,10 @@ string ABIFunctions::tupleEncoder(
|
||||
stackPos += sizeOnStack;
|
||||
}
|
||||
solAssert(headPos == headSize_, "");
|
||||
string valueParams = suffixedVariableNameList("value", stackPos, 0);
|
||||
string valueParams =
|
||||
_reversed ?
|
||||
suffixedVariableNameList("value", stackPos, 0) :
|
||||
suffixedVariableNameList("value", 0, stackPos);
|
||||
templ("valueParams", valueParams.empty() ? "" : ", " + valueParams);
|
||||
templ("encodeElements", encodeElements);
|
||||
|
||||
@@ -104,7 +109,8 @@ string ABIFunctions::tupleEncoder(
|
||||
|
||||
string ABIFunctions::tupleEncoderPacked(
|
||||
TypePointers const& _givenTypes,
|
||||
TypePointers const& _targetTypes
|
||||
TypePointers const& _targetTypes,
|
||||
bool _reversed
|
||||
)
|
||||
{
|
||||
EncodingOptions options;
|
||||
@@ -120,6 +126,8 @@ string ABIFunctions::tupleEncoderPacked(
|
||||
for (auto const& t: _targetTypes)
|
||||
functionName += t->identifier() + "_";
|
||||
functionName += options.toFunctionNameSuffix();
|
||||
if (_reversed)
|
||||
functionName += "_reversed";
|
||||
|
||||
return createFunction(functionName, [&]() {
|
||||
solAssert(!_givenTypes.empty(), "");
|
||||
@@ -158,7 +166,10 @@ string ABIFunctions::tupleEncoderPacked(
|
||||
encodeElements += elementTempl.render();
|
||||
stackPos += sizeOnStack;
|
||||
}
|
||||
string valueParams = suffixedVariableNameList("value", stackPos, 0);
|
||||
string valueParams =
|
||||
_reversed ?
|
||||
suffixedVariableNameList("value", stackPos, 0) :
|
||||
suffixedVariableNameList("value", 0, stackPos);
|
||||
templ("valueParams", valueParams.empty() ? "" : ", " + valueParams);
|
||||
templ("encodeElements", encodeElements);
|
||||
|
||||
|
||||
@@ -67,31 +67,53 @@ public:
|
||||
|
||||
/// @returns name of an assembly function to ABI-encode values of @a _givenTypes
|
||||
/// into memory, converting the types to @a _targetTypes on the fly.
|
||||
/// Parameters are: <headStart> <value_n> ... <value_1>, i.e.
|
||||
/// the layout on the stack is <value_1> ... <value_n> <headStart> with
|
||||
/// Parameters are: <headStart> <value_1> ... <value_n>, i.e.
|
||||
/// the layout on the stack is <value_n> ... <value_1> <headStart> with
|
||||
/// the top of the stack on the right.
|
||||
/// The values represent stack slots. If a type occupies more or less than one
|
||||
/// stack slot, it takes exactly that number of values.
|
||||
/// Returns a pointer to the end of the area written in memory.
|
||||
/// Does not allocate memory (does not change the free memory pointer), but writes
|
||||
/// to memory starting at $headStart and an unrestricted amount after that.
|
||||
/// If @reversed is true, the order of the variables after <headStart> is reversed.
|
||||
std::string tupleEncoder(
|
||||
TypePointers const& _givenTypes,
|
||||
TypePointers const& _targetTypes,
|
||||
bool _encodeAsLibraryTypes = false
|
||||
bool _encodeAsLibraryTypes = false,
|
||||
bool _reversed = false
|
||||
);
|
||||
|
||||
/// Specialization of tupleEncoder to _reversed = true
|
||||
std::string tupleEncoderReversed(
|
||||
TypePointers const& _givenTypes,
|
||||
TypePointers const& _targetTypes,
|
||||
bool _encodeAsLibraryTypes = false
|
||||
) {
|
||||
return tupleEncoder(_givenTypes, _targetTypes, _encodeAsLibraryTypes, true);
|
||||
}
|
||||
|
||||
/// @returns name of an assembly function to encode values of @a _givenTypes
|
||||
/// with packed encoding into memory, converting the types to @a _targetTypes on the fly.
|
||||
/// Parameters are: <memPos> <value_n> ... <value_1>, i.e.
|
||||
/// the layout on the stack is <value_1> ... <value_n> <memPos> with
|
||||
/// Parameters are: <memPos> <value_1> ... <value_n>, i.e.
|
||||
/// the layout on the stack is <value_n> ... <value_1> <memPos> with
|
||||
/// the top of the stack on the right.
|
||||
/// The values represent stack slots. If a type occupies more or less than one
|
||||
/// stack slot, it takes exactly that number of values.
|
||||
/// Returns a pointer to the end of the area written in memory.
|
||||
/// Does not allocate memory (does not change the free memory pointer), but writes
|
||||
/// to memory starting at memPos and an unrestricted amount after that.
|
||||
std::string tupleEncoderPacked(TypePointers const& _givenTypes, TypePointers const& _targetTypes);
|
||||
/// If @reversed is true, the order of the variables after <headStart> is reversed.
|
||||
std::string tupleEncoderPacked(
|
||||
TypePointers const& _givenTypes,
|
||||
TypePointers const& _targetTypes,
|
||||
bool _reversed = false
|
||||
);
|
||||
|
||||
/// Specialization of tupleEncoderPacked to _reversed = true
|
||||
std::string tupleEncoderPackedReversed(TypePointers const& _givenTypes, TypePointers const& _targetTypes)
|
||||
{
|
||||
return tupleEncoderPacked(_givenTypes, _targetTypes, true);
|
||||
}
|
||||
|
||||
/// @returns name of an assembly function to ABI-decode values of @a _types
|
||||
/// into memory. If @a _fromMemory is true, decodes from memory instead of
|
||||
|
||||
@@ -49,6 +49,9 @@ void Compiler::compileContract(
|
||||
m_runtimeSub = creationCompiler.compileConstructor(_contract, _otherCompilers);
|
||||
|
||||
m_context.optimise(m_optimiserSettings);
|
||||
|
||||
solAssert(m_context.requestedYulFunctionsRan(), "requestedYulFunctions() was not called.");
|
||||
solAssert(m_runtimeContext.requestedYulFunctionsRan(), "requestedYulFunctions() was not called.");
|
||||
}
|
||||
|
||||
std::shared_ptr<evmasm::Assembly> Compiler::runtimeAssemblyPtr() const
|
||||
|
||||
@@ -192,6 +192,9 @@ void CompilerContext::appendMissingLowLevelFunctions()
|
||||
|
||||
pair<string, set<string>> CompilerContext::requestedYulFunctions()
|
||||
{
|
||||
solAssert(!m_requestedYulFunctionsRan, "requestedYulFunctions called more than once.");
|
||||
m_requestedYulFunctionsRan = true;
|
||||
|
||||
set<string> empty;
|
||||
swap(empty, m_externallyUsedYulFunctions);
|
||||
return {m_yulFunctionCollector.requestedFunctions(), std::move(empty)};
|
||||
|
||||
@@ -167,6 +167,7 @@ public:
|
||||
/// Clears the internal list, i.e. calling it again will result in an
|
||||
/// empty return value.
|
||||
std::pair<std::string, std::set<std::string>> requestedYulFunctions();
|
||||
bool requestedYulFunctionsRan() const { return m_requestedYulFunctionsRan; }
|
||||
|
||||
/// Returns the distance of the given local variable from the bottom of the stack (of the current function).
|
||||
unsigned baseStackOffsetOfVariable(Declaration const& _declaration) const;
|
||||
@@ -389,6 +390,8 @@ private:
|
||||
YulUtilFunctions m_yulUtilFunctions;
|
||||
/// The queue of low-level functions to generate.
|
||||
std::queue<std::tuple<std::string, unsigned, unsigned, std::function<void(CompilerContext&)>>> m_lowLevelFunctionGenerationQueue;
|
||||
/// Flag to check that requestedYulFunctions() was called exactly once
|
||||
bool m_requestedYulFunctionsRan = false;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -559,8 +559,8 @@ void CompilerUtils::abiEncodeV2(
|
||||
|
||||
string encoderName =
|
||||
_padToWordBoundaries ?
|
||||
m_context.abiFunctions().tupleEncoder(_givenTypes, _targetTypes, _encodeAsLibraryTypes) :
|
||||
m_context.abiFunctions().tupleEncoderPacked(_givenTypes, _targetTypes);
|
||||
m_context.abiFunctions().tupleEncoderReversed(_givenTypes, _targetTypes, _encodeAsLibraryTypes) :
|
||||
m_context.abiFunctions().tupleEncoderPackedReversed(_givenTypes, _targetTypes);
|
||||
m_context.callYulFunction(encoderName, sizeOnStack(_givenTypes) + 1, 1);
|
||||
}
|
||||
|
||||
|
||||
@@ -103,8 +103,6 @@ void ContractCompiler::compileContract(
|
||||
// and adds the function to the compilation queue. Additionally internal functions,
|
||||
// which are referenced directly or indirectly will be added.
|
||||
appendFunctionSelector(_contract);
|
||||
// This processes the above populated queue until it is empty.
|
||||
appendMissingFunctions();
|
||||
}
|
||||
|
||||
size_t ContractCompiler::compileConstructor(
|
||||
@@ -159,10 +157,13 @@ void ContractCompiler::appendInitAndConstructorCode(ContractDefinition const& _c
|
||||
|
||||
if (FunctionDefinition const* constructor = _contract.constructor())
|
||||
appendConstructor(*constructor);
|
||||
else if (auto c = _contract.nextConstructor(m_context.mostDerivedContract()))
|
||||
appendBaseConstructor(*c);
|
||||
else
|
||||
{
|
||||
// Implicit constructors are always non-payable.
|
||||
appendCallValueCheck();
|
||||
if (auto c = _contract.nextConstructor(m_context.mostDerivedContract()))
|
||||
appendBaseConstructor(*c);
|
||||
}
|
||||
}
|
||||
|
||||
size_t ContractCompiler::packIntoContractCreator(ContractDefinition const& _contract)
|
||||
@@ -215,6 +216,9 @@ size_t ContractCompiler::deployLibrary(ContractDefinition const& _contract)
|
||||
solAssert(!!m_runtimeCompiler, "");
|
||||
solAssert(_contract.isLibrary(), "Tried to deploy contract as library.");
|
||||
|
||||
appendMissingFunctions();
|
||||
m_runtimeCompiler->appendMissingFunctions();
|
||||
|
||||
CompilerContext::LocationSetter locationSetter(m_context, _contract);
|
||||
|
||||
solAssert(m_context.runtimeSub() != size_t(-1), "Runtime sub not registered");
|
||||
@@ -586,13 +590,13 @@ bool ContractCompiler::visit(FunctionDefinition const& _function)
|
||||
if (!_function.isConstructor())
|
||||
// adding 1 for return address.
|
||||
m_context.adjustStackOffset(parametersSize + 1);
|
||||
for (ASTPointer<VariableDeclaration const> const& variable: _function.parameters())
|
||||
for (ASTPointer<VariableDeclaration> const& variable: _function.parameters())
|
||||
{
|
||||
m_context.addVariable(*variable, parametersSize);
|
||||
parametersSize -= variable->annotation().type->sizeOnStack();
|
||||
}
|
||||
|
||||
for (ASTPointer<VariableDeclaration const> const& variable: _function.returnParameters())
|
||||
for (ASTPointer<VariableDeclaration> const& variable: _function.returnParameters())
|
||||
appendStackVariableInitialisation(*variable);
|
||||
|
||||
if (_function.isConstructor())
|
||||
@@ -649,7 +653,7 @@ bool ContractCompiler::visit(FunctionDefinition const& _function)
|
||||
if (stackLayout[i] != i)
|
||||
solAssert(false, "Invalid stack layout on cleanup.");
|
||||
|
||||
for (ASTPointer<VariableDeclaration const> const& variable: _function.parameters() + _function.returnParameters())
|
||||
for (ASTPointer<VariableDeclaration> const& variable: _function.parameters() + _function.returnParameters())
|
||||
m_context.removeVariable(*variable);
|
||||
|
||||
m_context.adjustStackOffset(-(int)c_returnValuesSize);
|
||||
|
||||
@@ -36,7 +36,6 @@
|
||||
#include <libsolutil/Whiskers.h>
|
||||
|
||||
#include <boost/algorithm/string/replace.hpp>
|
||||
#include <boost/range/adaptor/reversed.hpp>
|
||||
#include <numeric>
|
||||
#include <utility>
|
||||
|
||||
@@ -1591,6 +1590,16 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
|
||||
result ^= fromBigEndian<uint64_t>(function.first.ref());
|
||||
m_context << (u256{result} << (256 - 32));
|
||||
}
|
||||
else if (member == "min" || member == "max")
|
||||
{
|
||||
MagicType const* arg = dynamic_cast<MagicType const*>(_memberAccess.expression().annotation().type);
|
||||
IntegerType const* integerType = dynamic_cast<IntegerType const*>(arg->typeArgument());
|
||||
|
||||
if (member == "min")
|
||||
m_context << integerType->min();
|
||||
else
|
||||
m_context << integerType->max();
|
||||
}
|
||||
else if ((set<string>{"encode", "encodePacked", "encodeWithSelector", "encodeWithSignature", "decode"}).count(member))
|
||||
{
|
||||
// no-op
|
||||
|
||||
@@ -23,8 +23,6 @@
|
||||
|
||||
#include <liblangutil/Exceptions.h>
|
||||
|
||||
#include <boost/algorithm/string/join.hpp>
|
||||
#include <boost/range/adaptor/reversed.hpp>
|
||||
|
||||
using namespace std;
|
||||
using namespace solidity;
|
||||
|
||||
@@ -28,9 +28,6 @@
|
||||
#include <libsolutil/Whiskers.h>
|
||||
#include <libsolutil/StringUtils.h>
|
||||
|
||||
#include <boost/algorithm/string/join.hpp>
|
||||
#include <boost/range/adaptor/reversed.hpp>
|
||||
|
||||
using namespace std;
|
||||
using namespace solidity;
|
||||
using namespace solidity::util;
|
||||
@@ -299,9 +296,6 @@ string YulUtilFunctions::shiftRightFunction(size_t _numBits)
|
||||
|
||||
string YulUtilFunctions::shiftRightFunctionDynamic()
|
||||
{
|
||||
// Note that if this is extended with signed shifts,
|
||||
// the opcodes SAR and SDIV behave differently with regards to rounding!
|
||||
|
||||
string const functionName = "shift_right_unsigned_dynamic";
|
||||
return m_functionCollector.createFunction(functionName, [&]() {
|
||||
return
|
||||
@@ -321,6 +315,86 @@ string YulUtilFunctions::shiftRightFunctionDynamic()
|
||||
});
|
||||
}
|
||||
|
||||
string YulUtilFunctions::shiftRightSignedFunctionDynamic()
|
||||
{
|
||||
string const functionName = "shift_right_signed_dynamic";
|
||||
return m_functionCollector.createFunction(functionName, [&]() {
|
||||
return
|
||||
Whiskers(R"(
|
||||
function <functionName>(bits, value) -> result {
|
||||
<?hasShifts>
|
||||
result := sar(bits, value)
|
||||
<!hasShifts>
|
||||
let divisor := exp(2, bits)
|
||||
let xor_mask := sub(0, slt(value, 0))
|
||||
result := xor(div(xor(value, xor_mask), divisor), xor_mask)
|
||||
// combined version of
|
||||
// switch slt(value, 0)
|
||||
// case 0 { result := div(value, divisor) }
|
||||
// default { result := not(div(not(value), divisor)) }
|
||||
</hasShifts>
|
||||
}
|
||||
)")
|
||||
("functionName", functionName)
|
||||
("hasShifts", m_evmVersion.hasBitwiseShifting())
|
||||
.render();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
string YulUtilFunctions::typedShiftLeftFunction(Type const& _type, Type const& _amountType)
|
||||
{
|
||||
solAssert(_type.category() == Type::Category::FixedBytes || _type.category() == Type::Category::Integer, "");
|
||||
solAssert(_amountType.category() == Type::Category::Integer, "");
|
||||
string const functionName = "shift_left_" + _type.identifier() + "_" + _amountType.identifier();
|
||||
return m_functionCollector.createFunction(functionName, [&]() {
|
||||
return
|
||||
Whiskers(R"(
|
||||
function <functionName>(value, bits) -> result {
|
||||
bits := <cleanAmount>(bits)
|
||||
<?amountSigned>
|
||||
if slt(bits, 0) { invalid() }
|
||||
</amountSigned>
|
||||
result := <cleanup>(<shift>(bits, value))
|
||||
}
|
||||
)")
|
||||
("functionName", functionName)
|
||||
("amountSigned", dynamic_cast<IntegerType const&>(_amountType).isSigned())
|
||||
("cleanAmount", cleanupFunction(_amountType))
|
||||
("shift", shiftLeftFunctionDynamic())
|
||||
("cleanup", cleanupFunction(_type))
|
||||
.render();
|
||||
});
|
||||
}
|
||||
|
||||
string YulUtilFunctions::typedShiftRightFunction(Type const& _type, Type const& _amountType)
|
||||
{
|
||||
solAssert(_type.category() == Type::Category::FixedBytes || _type.category() == Type::Category::Integer, "");
|
||||
solAssert(_amountType.category() == Type::Category::Integer, "");
|
||||
IntegerType const* integerType = dynamic_cast<IntegerType const*>(&_type);
|
||||
bool valueSigned = integerType && integerType->isSigned();
|
||||
|
||||
string const functionName = "shift_right_" + _type.identifier() + "_" + _amountType.identifier();
|
||||
return m_functionCollector.createFunction(functionName, [&]() {
|
||||
return
|
||||
Whiskers(R"(
|
||||
function <functionName>(value, bits) -> result {
|
||||
bits := <cleanAmount>(bits)
|
||||
<?amountSigned>
|
||||
if slt(bits, 0) { invalid() }
|
||||
</amountSigned>
|
||||
result := <cleanup>(<shift>(bits, <cleanup>(value)))
|
||||
}
|
||||
)")
|
||||
("functionName", functionName)
|
||||
("amountSigned", dynamic_cast<IntegerType const&>(_amountType).isSigned())
|
||||
("cleanAmount", cleanupFunction(_amountType))
|
||||
("shift", valueSigned ? shiftRightSignedFunctionDynamic() : shiftRightFunctionDynamic())
|
||||
("cleanup", cleanupFunction(_type))
|
||||
.render();
|
||||
});
|
||||
}
|
||||
|
||||
string YulUtilFunctions::updateByteSliceFunction(size_t _numBytes, size_t _shiftBytes)
|
||||
{
|
||||
solAssert(_numBytes <= 32, "");
|
||||
@@ -393,6 +467,8 @@ string YulUtilFunctions::overflowCheckedIntAddFunction(IntegerType const& _type)
|
||||
return
|
||||
Whiskers(R"(
|
||||
function <functionName>(x, y) -> sum {
|
||||
x := <cleanupFunction>(x)
|
||||
y := <cleanupFunction>(y)
|
||||
<?signed>
|
||||
// overflow, if x >= 0 and y > (maxValue - x)
|
||||
if and(iszero(slt(x, 0)), sgt(y, sub(<maxValue>, x))) { revert(0, 0) }
|
||||
@@ -409,6 +485,7 @@ string YulUtilFunctions::overflowCheckedIntAddFunction(IntegerType const& _type)
|
||||
("signed", _type.isSigned())
|
||||
("maxValue", toCompactHexWithPrefix(u256(_type.maxValue())))
|
||||
("minValue", toCompactHexWithPrefix(u256(_type.minValue())))
|
||||
("cleanupFunction", cleanupFunction(_type))
|
||||
.render();
|
||||
});
|
||||
}
|
||||
@@ -421,6 +498,8 @@ string YulUtilFunctions::overflowCheckedIntMulFunction(IntegerType const& _type)
|
||||
// Multiplication by zero could be treated separately and directly return zero.
|
||||
Whiskers(R"(
|
||||
function <functionName>(x, y) -> product {
|
||||
x := <cleanupFunction>(x)
|
||||
y := <cleanupFunction>(y)
|
||||
<?signed>
|
||||
// overflow, if x > 0, y > 0 and x > (maxValue / y)
|
||||
if and(and(sgt(x, 0), sgt(y, 0)), gt(x, div(<maxValue>, y))) { revert(0, 0) }
|
||||
@@ -441,6 +520,7 @@ string YulUtilFunctions::overflowCheckedIntMulFunction(IntegerType const& _type)
|
||||
("signed", _type.isSigned())
|
||||
("maxValue", toCompactHexWithPrefix(u256(_type.maxValue())))
|
||||
("minValue", toCompactHexWithPrefix(u256(_type.minValue())))
|
||||
("cleanupFunction", cleanupFunction(_type))
|
||||
.render();
|
||||
});
|
||||
}
|
||||
@@ -452,6 +532,8 @@ string YulUtilFunctions::overflowCheckedIntDivFunction(IntegerType const& _type)
|
||||
return
|
||||
Whiskers(R"(
|
||||
function <functionName>(x, y) -> r {
|
||||
x := <cleanupFunction>(x)
|
||||
y := <cleanupFunction>(y)
|
||||
if iszero(y) { revert(0, 0) }
|
||||
<?signed>
|
||||
// overflow for minVal / -1
|
||||
@@ -466,6 +548,7 @@ string YulUtilFunctions::overflowCheckedIntDivFunction(IntegerType const& _type)
|
||||
("functionName", functionName)
|
||||
("signed", _type.isSigned())
|
||||
("minVal", toCompactHexWithPrefix(u256(_type.minValue())))
|
||||
("cleanupFunction", cleanupFunction(_type))
|
||||
.render();
|
||||
});
|
||||
}
|
||||
@@ -477,12 +560,15 @@ string YulUtilFunctions::checkedIntModFunction(IntegerType const& _type)
|
||||
return
|
||||
Whiskers(R"(
|
||||
function <functionName>(x, y) -> r {
|
||||
x := <cleanupFunction>(x)
|
||||
y := <cleanupFunction>(y)
|
||||
if iszero(y) { revert(0, 0) }
|
||||
r := <?signed>s</signed>mod(x, y)
|
||||
}
|
||||
)")
|
||||
("functionName", functionName)
|
||||
("signed", _type.isSigned())
|
||||
("cleanupFunction", cleanupFunction(_type))
|
||||
.render();
|
||||
});
|
||||
}
|
||||
@@ -494,6 +580,8 @@ string YulUtilFunctions::overflowCheckedIntSubFunction(IntegerType const& _type)
|
||||
return
|
||||
Whiskers(R"(
|
||||
function <functionName>(x, y) -> diff {
|
||||
x := <cleanupFunction>(x)
|
||||
y := <cleanupFunction>(y)
|
||||
<?signed>
|
||||
// underflow, if y >= 0 and x < (minValue + y)
|
||||
if and(iszero(slt(y, 0)), slt(x, add(<minValue>, y))) { revert(0, 0) }
|
||||
@@ -509,6 +597,7 @@ string YulUtilFunctions::overflowCheckedIntSubFunction(IntegerType const& _type)
|
||||
("signed", _type.isSigned())
|
||||
("maxValue", toCompactHexWithPrefix(u256(_type.maxValue())))
|
||||
("minValue", toCompactHexWithPrefix(u256(_type.minValue())))
|
||||
("cleanupFunction", cleanupFunction(_type))
|
||||
.render();
|
||||
});
|
||||
}
|
||||
@@ -1033,13 +1122,12 @@ string YulUtilFunctions::mappingIndexAccessFunction(MappingType const& _mappingT
|
||||
return m_functionCollector.createFunction(functionName, [&]() {
|
||||
if (_mappingType.keyType()->isDynamicallySized())
|
||||
return Whiskers(R"(
|
||||
function <functionName>(slot <comma> <key>) -> dataSlot {
|
||||
dataSlot := <hash>(slot <comma> <key>)
|
||||
function <functionName>(slot <?+key>,</+key> <key>) -> dataSlot {
|
||||
dataSlot := <hash>(<key> <?+key>,</+key> slot)
|
||||
}
|
||||
)")
|
||||
("functionName", functionName)
|
||||
("key", _keyType.sizeOnStack() > 0 ? "key" : "")
|
||||
("comma", _keyType.sizeOnStack() > 0 ? "," : "")
|
||||
("hash", packedHashFunction(
|
||||
{&_keyType, TypeProvider::uint256()},
|
||||
{_mappingType.keyType(), TypeProvider::uint256()}
|
||||
@@ -1764,8 +1852,18 @@ string YulUtilFunctions::cleanupFunction(Type const& _type)
|
||||
solUnimplemented("Fixed point types not implemented.");
|
||||
break;
|
||||
case Type::Category::Function:
|
||||
solAssert(dynamic_cast<FunctionType const&>(_type).kind() == FunctionType::Kind::External, "");
|
||||
templ("body", "cleaned := " + cleanupFunction(FixedBytesType(24)) + "(value)");
|
||||
switch (dynamic_cast<FunctionType const&>(_type).kind())
|
||||
{
|
||||
case FunctionType::Kind::External:
|
||||
templ("body", "cleaned := " + cleanupFunction(FixedBytesType(24)) + "(value)");
|
||||
break;
|
||||
case FunctionType::Kind::Internal:
|
||||
templ("body", "cleaned := value");
|
||||
break;
|
||||
default:
|
||||
solAssert(false, "");
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case Type::Category::Array:
|
||||
case Type::Category::Struct:
|
||||
@@ -1938,14 +2036,16 @@ std::string YulUtilFunctions::decrementCheckedFunction(Type const& _type)
|
||||
|
||||
return Whiskers(R"(
|
||||
function <functionName>(value) -> ret {
|
||||
value := <cleanupFunction>(value)
|
||||
if <lt>(value, <minval>) { revert(0,0) }
|
||||
ret := sub(value, 1)
|
||||
}
|
||||
)")
|
||||
("functionName", functionName)
|
||||
("minval", toCompactHexWithPrefix(minintval))
|
||||
("lt", type.isSigned() ? "slt" : "lt")
|
||||
.render();
|
||||
("functionName", functionName)
|
||||
("minval", toCompactHexWithPrefix(minintval))
|
||||
("lt", type.isSigned() ? "slt" : "lt")
|
||||
("cleanupFunction", cleanupFunction(_type))
|
||||
.render();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1966,14 +2066,16 @@ std::string YulUtilFunctions::incrementCheckedFunction(Type const& _type)
|
||||
|
||||
return Whiskers(R"(
|
||||
function <functionName>(value) -> ret {
|
||||
value := <cleanupFunction>(value)
|
||||
if <gt>(value, <maxval>) { revert(0,0) }
|
||||
ret := add(value, 1)
|
||||
}
|
||||
)")
|
||||
("functionName", functionName)
|
||||
("maxval", toCompactHexWithPrefix(maxintval))
|
||||
("gt", type.isSigned() ? "sgt" : "gt")
|
||||
.render();
|
||||
("functionName", functionName)
|
||||
("maxval", toCompactHexWithPrefix(maxintval))
|
||||
("gt", type.isSigned() ? "sgt" : "gt")
|
||||
("cleanupFunction", cleanupFunction(_type))
|
||||
.render();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1988,15 +2090,17 @@ string YulUtilFunctions::negateNumberCheckedFunction(Type const& _type)
|
||||
|
||||
return m_functionCollector.createFunction(functionName, [&]() {
|
||||
return Whiskers(R"(
|
||||
function <functionName>(_value) -> ret {
|
||||
if slt(_value, <minval>) { revert(0,0) }
|
||||
ret := sub(0, _value)
|
||||
function <functionName>(value) -> ret {
|
||||
value := <cleanupFunction>(value)
|
||||
if slt(value, <minval>) { revert(0,0) }
|
||||
ret := sub(0, value)
|
||||
}
|
||||
)")
|
||||
("functionName", functionName)
|
||||
("minval", toCompactHexWithPrefix(minintval))
|
||||
.render();
|
||||
});
|
||||
("functionName", functionName)
|
||||
("minval", toCompactHexWithPrefix(minintval))
|
||||
("cleanupFunction", cleanupFunction(_type))
|
||||
.render();
|
||||
});
|
||||
}
|
||||
|
||||
string YulUtilFunctions::zeroValueFunction(Type const& _type, bool _splitFunctionTypes)
|
||||
@@ -2214,7 +2318,6 @@ string YulUtilFunctions::readFromMemoryOrCalldata(Type const& _type, bool _fromC
|
||||
}
|
||||
|
||||
solAssert(_type.isValueType(), "");
|
||||
|
||||
if (auto const* funType = dynamic_cast<FunctionType const*>(&_type))
|
||||
if (funType->kind() == FunctionType::Kind::External)
|
||||
return Whiskers(R"(
|
||||
@@ -2228,18 +2331,23 @@ string YulUtilFunctions::readFromMemoryOrCalldata(Type const& _type, bool _fromC
|
||||
("splitFunction", splitExternalFunctionIdFunction())
|
||||
.render();
|
||||
|
||||
|
||||
return Whiskers(R"(
|
||||
function <functionName>(memPtr) -> value {
|
||||
value := <load>(memPtr)
|
||||
<?needsValidation>
|
||||
function <functionName>(ptr) -> value {
|
||||
<?fromCalldata>
|
||||
value := calldataload(ptr)
|
||||
<validate>(value)
|
||||
</needsValidation>
|
||||
<!fromCalldata>
|
||||
value := <cleanup>(mload(ptr))
|
||||
</fromCalldata>
|
||||
}
|
||||
)")
|
||||
("functionName", functionName)
|
||||
("load", _fromCalldata ? "calldataload" : "mload")
|
||||
("needsValidation", _fromCalldata)
|
||||
("validate", _fromCalldata ? validatorFunction(_type) : "")
|
||||
("fromCalldata", _fromCalldata)
|
||||
("validate", validatorFunction(_type))
|
||||
// Byte array elements generally need cleanup.
|
||||
// Other types are cleaned as well to account for dirty memory e.g. due to inline assembly.
|
||||
("cleanup", cleanupFunction(_type))
|
||||
.render();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -81,6 +81,14 @@ public:
|
||||
std::string shiftLeftFunctionDynamic();
|
||||
std::string shiftRightFunction(size_t _numBits);
|
||||
std::string shiftRightFunctionDynamic();
|
||||
std::string shiftRightSignedFunctionDynamic();
|
||||
|
||||
/// @returns the name of a function that performs a left shift and subsequent cleanup
|
||||
/// and, if needed, prior cleanup.
|
||||
/// If the amount to shift by is signed, a check for negativeness is performed.
|
||||
/// signature: (value, amountToShift) -> result
|
||||
std::string typedShiftLeftFunction(Type const& _type, Type const& _amountType);
|
||||
std::string typedShiftRightFunction(Type const& _type, Type const& _amountType);
|
||||
|
||||
/// @returns the name of a function which replaces the
|
||||
/// _numBytes bytes starting at byte position _shiftBytes (counted from the least significant
|
||||
@@ -204,7 +212,7 @@ public:
|
||||
std::string readFromStorage(Type const& _type, size_t _offset, bool _splitFunctionTypes);
|
||||
std::string readFromStorageDynamic(Type const& _type, bool _splitFunctionTypes);
|
||||
|
||||
/// @returns a function that reads a value type from memory.
|
||||
/// @returns a function that reads a value type from memory. Performs cleanup.
|
||||
/// signature: (addr) -> value
|
||||
std::string readFromMemory(Type const& _type);
|
||||
/// @returns a function that reads a value type from calldata.
|
||||
|
||||
@@ -38,9 +38,6 @@
|
||||
|
||||
#include <liblangutil/SourceReferenceFormatter.h>
|
||||
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include <boost/range/adaptor/reversed.hpp>
|
||||
|
||||
#include <sstream>
|
||||
|
||||
using namespace std;
|
||||
@@ -172,24 +169,24 @@ string IRGenerator::generateFunction(FunctionDefinition const& _function)
|
||||
string functionName = m_context.functionName(_function);
|
||||
return m_context.functionCollector().createFunction(functionName, [&]() {
|
||||
Whiskers t(R"(
|
||||
function <functionName>(<params>) <returns> {
|
||||
function <functionName>(<params>)<?+retParams> -> <retParams></+retParams> {
|
||||
<initReturnVariables>
|
||||
<body>
|
||||
}
|
||||
)");
|
||||
t("functionName", functionName);
|
||||
string params;
|
||||
vector<string> params;
|
||||
for (auto const& varDecl: _function.parameters())
|
||||
params += (params.empty() ? "" : ", ") + m_context.addLocalVariable(*varDecl).commaSeparatedList();
|
||||
t("params", params);
|
||||
string retParams;
|
||||
params += m_context.addLocalVariable(*varDecl).stackSlots();
|
||||
t("params", joinHumanReadable(params));
|
||||
vector<string> retParams;
|
||||
string retInit;
|
||||
for (auto const& varDecl: _function.returnParameters())
|
||||
{
|
||||
retParams += (retParams.empty() ? "" : ", ") + m_context.addLocalVariable(*varDecl).commaSeparatedList();
|
||||
retParams += m_context.addLocalVariable(*varDecl).stackSlots();
|
||||
retInit += generateInitialAssignment(*varDecl);
|
||||
}
|
||||
t("returns", retParams.empty() ? "" : " -> " + retParams);
|
||||
t("retParams", joinHumanReadable(retParams));
|
||||
t("initReturnVariables", retInit);
|
||||
t("body", generate(_function.body()));
|
||||
return t.render();
|
||||
@@ -202,11 +199,11 @@ string IRGenerator::generateGetter(VariableDeclaration const& _varDecl)
|
||||
|
||||
Type const* type = _varDecl.annotation().type;
|
||||
|
||||
solAssert(!_varDecl.isConstant(), "");
|
||||
solAssert(_varDecl.isStateVariable(), "");
|
||||
|
||||
if (auto const* mappingType = dynamic_cast<MappingType const*>(type))
|
||||
return m_context.functionCollector().createFunction(functionName, [&]() {
|
||||
solAssert(!_varDecl.isConstant() && !_varDecl.immutable(), "");
|
||||
pair<u256, unsigned> slot_offset = m_context.storageLocationOfVariable(_varDecl);
|
||||
solAssert(slot_offset.second == 0, "");
|
||||
FunctionType funType(_varDecl);
|
||||
@@ -268,6 +265,16 @@ string IRGenerator::generateGetter(VariableDeclaration const& _varDecl)
|
||||
("id", to_string(_varDecl.id()))
|
||||
.render();
|
||||
}
|
||||
else if (_varDecl.isConstant())
|
||||
return Whiskers(R"(
|
||||
function <functionName>() -> <ret> {
|
||||
<ret> := <constantValueFunction>()
|
||||
}
|
||||
)")
|
||||
("functionName", functionName)
|
||||
("constantValueFunction", IRGeneratorForStatements(m_context, m_utils).constantValueFunction(_varDecl))
|
||||
("ret", suffixedVariableNameList("ret_", 0, _varDecl.type()->sizeOnStack()))
|
||||
.render();
|
||||
else
|
||||
{
|
||||
pair<u256, unsigned> slot_offset = m_context.storageLocationOfVariable(_varDecl);
|
||||
@@ -293,31 +300,35 @@ string IRGenerator::generateInitialAssignment(VariableDeclaration const& _varDec
|
||||
return generator.code();
|
||||
}
|
||||
|
||||
pair<string, map<ContractDefinition const*, string>> IRGenerator::evaluateConstructorArguments(
|
||||
pair<string, map<ContractDefinition const*, vector<string>>> IRGenerator::evaluateConstructorArguments(
|
||||
ContractDefinition const& _contract
|
||||
)
|
||||
{
|
||||
map<ContractDefinition const*, string> constructorParams;
|
||||
map<ContractDefinition const*, vector<string>> constructorParams;
|
||||
vector<pair<ContractDefinition const*, std::vector<ASTPointer<Expression>>const *>> baseConstructorArguments;
|
||||
|
||||
for (ASTPointer<InheritanceSpecifier> const& base: _contract.baseContracts())
|
||||
if (FunctionDefinition const* baseConstructor = dynamic_cast<ContractDefinition const*>(
|
||||
base->name().annotation().referencedDeclaration
|
||||
)->constructor(); baseConstructor && base->arguments())
|
||||
base->name().annotation().referencedDeclaration
|
||||
)->constructor(); baseConstructor && base->arguments())
|
||||
baseConstructorArguments.emplace_back(
|
||||
dynamic_cast<ContractDefinition const*>(baseConstructor->scope()),
|
||||
base->arguments()
|
||||
dynamic_cast<ContractDefinition const*>(baseConstructor->scope()),
|
||||
base->arguments()
|
||||
);
|
||||
|
||||
if (FunctionDefinition const* constructor = _contract.constructor())
|
||||
for (auto const& modifier: constructor->modifiers())
|
||||
if (FunctionDefinition const* baseConstructor = dynamic_cast<ContractDefinition const*>(
|
||||
for (ASTPointer<ModifierInvocation> const& modifier: constructor->modifiers())
|
||||
if (auto const* baseContract = dynamic_cast<ContractDefinition const*>(
|
||||
modifier->name()->annotation().referencedDeclaration
|
||||
)->constructor(); baseConstructor && modifier->arguments())
|
||||
baseConstructorArguments.emplace_back(
|
||||
dynamic_cast<ContractDefinition const*>(baseConstructor->scope()),
|
||||
modifier->arguments()
|
||||
);
|
||||
))
|
||||
if (
|
||||
FunctionDefinition const* baseConstructor = baseContract->constructor();
|
||||
baseConstructor && modifier->arguments()
|
||||
)
|
||||
baseConstructorArguments.emplace_back(
|
||||
dynamic_cast<ContractDefinition const*>(baseConstructor->scope()),
|
||||
modifier->arguments()
|
||||
);
|
||||
|
||||
IRGeneratorForStatements generator{m_context, m_utils};
|
||||
for (auto&& [baseContract, arguments]: baseConstructorArguments)
|
||||
@@ -327,11 +338,11 @@ pair<string, map<ContractDefinition const*, string>> IRGenerator::evaluateConstr
|
||||
{
|
||||
vector<string> params;
|
||||
for (size_t i = 0; i < arguments->size(); ++i)
|
||||
params.emplace_back(generator.evaluateExpression(
|
||||
*(arguments->at(i)),
|
||||
*(baseContract->constructor()->parameters()[i]->type())
|
||||
).commaSeparatedList());
|
||||
constructorParams[baseContract] = joinHumanReadable(params);
|
||||
params += generator.evaluateExpression(
|
||||
*(arguments->at(i)),
|
||||
*(baseContract->constructor()->parameters()[i]->type())
|
||||
).stackSlots();
|
||||
constructorParams[baseContract] = std::move(params);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -352,16 +363,16 @@ string IRGenerator::initStateVariables(ContractDefinition const& _contract)
|
||||
void IRGenerator::generateImplicitConstructors(ContractDefinition const& _contract)
|
||||
{
|
||||
auto listAllParams = [&](
|
||||
map<ContractDefinition const*, string> const& baseParams) -> string
|
||||
map<ContractDefinition const*, vector<string>> const& baseParams) -> vector<string>
|
||||
{
|
||||
vector<string> params;
|
||||
for (ContractDefinition const* contract: _contract.annotation().linearizedBaseContracts)
|
||||
if (baseParams.count(contract))
|
||||
params.emplace_back(baseParams.at(contract));
|
||||
return joinHumanReadable(params);
|
||||
params += baseParams.at(contract);
|
||||
return params;
|
||||
};
|
||||
|
||||
map<ContractDefinition const*, string> baseConstructorParams;
|
||||
map<ContractDefinition const*, vector<string>> baseConstructorParams;
|
||||
for (size_t i = 0; i < _contract.annotation().linearizedBaseContracts.size(); ++i)
|
||||
{
|
||||
ContractDefinition const* contract = _contract.annotation().linearizedBaseContracts[i];
|
||||
@@ -376,16 +387,16 @@ void IRGenerator::generateImplicitConstructors(ContractDefinition const& _contra
|
||||
<userDefinedConstructorBody>
|
||||
}
|
||||
)");
|
||||
string params;
|
||||
vector<string> params;
|
||||
if (contract->constructor())
|
||||
for (ASTPointer<VariableDeclaration> const& varDecl: contract->constructor()->parameters())
|
||||
params += (params.empty() ? "" : ", ") + m_context.addLocalVariable(*varDecl).commaSeparatedList();
|
||||
t("params", params);
|
||||
string baseParamsString = listAllParams(baseConstructorParams);
|
||||
t("baseParams", baseParamsString);
|
||||
t("comma", !params.empty() && !baseParamsString.empty() ? ", " : "");
|
||||
params += m_context.addLocalVariable(*varDecl).stackSlots();
|
||||
t("params", joinHumanReadable(params));
|
||||
vector<string> baseParams = listAllParams(baseConstructorParams);
|
||||
t("baseParams", joinHumanReadable(baseParams));
|
||||
t("comma", !params.empty() && !baseParams.empty() ? ", " : "");
|
||||
t("functionName", implicitConstructorName(*contract));
|
||||
pair<string, map<ContractDefinition const*, string>> evaluatedArgs = evaluateConstructorArguments(*contract);
|
||||
pair<string, map<ContractDefinition const*, vector<string>>> evaluatedArgs = evaluateConstructorArguments(*contract);
|
||||
baseConstructorParams.insert(evaluatedArgs.second.begin(), evaluatedArgs.second.end());
|
||||
t("evalBaseArguments", evaluatedArgs.first);
|
||||
if (i < _contract.annotation().linearizedBaseContracts.size() - 1)
|
||||
@@ -393,7 +404,7 @@ void IRGenerator::generateImplicitConstructors(ContractDefinition const& _contra
|
||||
t("hasNextConstructor", true);
|
||||
ContractDefinition const* nextContract = _contract.annotation().linearizedBaseContracts[i + 1];
|
||||
t("nextConstructor", implicitConstructorName(*nextContract));
|
||||
t("nextParams", listAllParams(baseConstructorParams));
|
||||
t("nextParams", joinHumanReadable(listAllParams(baseConstructorParams)));
|
||||
}
|
||||
else
|
||||
t("hasNextConstructor", false);
|
||||
@@ -468,10 +479,10 @@ string IRGenerator::dispatchRoutine(ContractDefinition const& _contract)
|
||||
{
|
||||
// <functionName>
|
||||
<callValueCheck>
|
||||
<assignToParams> <abiDecode>(4, calldatasize())
|
||||
<assignToRetParams> <function>(<params>)
|
||||
<?+params>let <params> := </+params> <abiDecode>(4, calldatasize())
|
||||
<?+retParams>let <retParams> := </+retParams> <function>(<params>)
|
||||
let memPos := <allocate>(0)
|
||||
let memEnd := <abiEncode>(memPos <comma> <retParams>)
|
||||
let memEnd := <abiEncode>(memPos <?+retParams>,</+retParams> <retParams>)
|
||||
return(memPos, sub(memEnd, memPos))
|
||||
}
|
||||
</cases>
|
||||
@@ -493,13 +504,11 @@ string IRGenerator::dispatchRoutine(ContractDefinition const& _contract)
|
||||
|
||||
unsigned paramVars = make_shared<TupleType>(type->parameterTypes())->sizeOnStack();
|
||||
unsigned retVars = make_shared<TupleType>(type->returnParameterTypes())->sizeOnStack();
|
||||
templ["assignToParams"] = paramVars == 0 ? "" : "let " + suffixedVariableNameList("param_", 0, paramVars) + " := ";
|
||||
templ["assignToRetParams"] = retVars == 0 ? "" : "let " + suffixedVariableNameList("ret_", 0, retVars) + " := ";
|
||||
|
||||
ABIFunctions abiFunctions(m_evmVersion, m_context.revertStrings(), m_context.functionCollector());
|
||||
templ["abiDecode"] = abiFunctions.tupleDecoder(type->parameterTypes());
|
||||
templ["params"] = suffixedVariableNameList("param_", 0, paramVars);
|
||||
templ["retParams"] = suffixedVariableNameList("ret_", retVars, 0);
|
||||
templ["retParams"] = suffixedVariableNameList("ret_", 0, retVars);
|
||||
|
||||
if (FunctionDefinition const* funDef = dynamic_cast<FunctionDefinition const*>(&type->declaration()))
|
||||
templ["function"] = m_context.enqueueFunctionForCodeGeneration(*funDef);
|
||||
@@ -510,7 +519,6 @@ string IRGenerator::dispatchRoutine(ContractDefinition const& _contract)
|
||||
|
||||
templ["allocate"] = m_utils.allocationFunction();
|
||||
templ["abiEncode"] = abiFunctions.tupleEncoder(type->returnParameterTypes(), type->returnParameterTypes(), false);
|
||||
templ["comma"] = retVars == 0 ? "" : ", ";
|
||||
}
|
||||
t("cases", functions);
|
||||
if (FunctionDefinition const* fallback = _contract.fallbackFunction())
|
||||
|
||||
@@ -81,9 +81,8 @@ private:
|
||||
/// Evaluates constructor's arguments for all base contracts (listed in inheritance specifiers) of
|
||||
/// @a _contract
|
||||
/// @returns Pair of expressions needed to evaluate params and list of parameters in a map contract -> params
|
||||
std::pair<std::string, std::map<ContractDefinition const*, std::string>> evaluateConstructorArguments(
|
||||
ContractDefinition const& _contract
|
||||
);
|
||||
std::pair<std::string, std::map<ContractDefinition const*, std::vector<std::string>>>
|
||||
evaluateConstructorArguments(ContractDefinition const& _contract);
|
||||
|
||||
/// Initializes state variables of
|
||||
/// @a _contract
|
||||
|
||||
@@ -42,7 +42,6 @@
|
||||
#include <libsolutil/Keccak256.h>
|
||||
#include <libsolutil/Visitor.h>
|
||||
|
||||
#include <boost/range/adaptor/reversed.hpp>
|
||||
#include <boost/range/adaptor/transformed.hpp>
|
||||
|
||||
using namespace std;
|
||||
@@ -117,10 +116,12 @@ struct CopyTranslate: public yul::ASTCopier
|
||||
reference.isOffset == false && reference.isSlot == false,
|
||||
"Should not be called for offset/slot"
|
||||
);
|
||||
auto const& var = m_context.localVariable(*varDecl);
|
||||
solAssert(var.type().sizeOnStack() == 1, "");
|
||||
|
||||
return yul::Identifier{
|
||||
_identifier.location,
|
||||
yul::YulString{m_context.localVariable(*varDecl).name()}
|
||||
yul::YulString{var.commaSeparatedList()}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -178,6 +179,28 @@ IRVariable IRGeneratorForStatements::evaluateExpression(Expression const& _expre
|
||||
return variable;
|
||||
}
|
||||
|
||||
string IRGeneratorForStatements::constantValueFunction(VariableDeclaration const& _constant)
|
||||
{
|
||||
string functionName = "constant_" + _constant.name() + "_" + to_string(_constant.id());
|
||||
return m_context.functionCollector().createFunction(functionName, [&] {
|
||||
Whiskers templ(R"(
|
||||
function <functionName>() -> <ret> {
|
||||
<code>
|
||||
<ret> := <value>
|
||||
}
|
||||
)");
|
||||
templ("functionName", functionName);
|
||||
IRGeneratorForStatements generator(m_context, m_utils);
|
||||
solAssert(_constant.value(), "");
|
||||
Type const& constantType = *_constant.type();
|
||||
templ("value", generator.evaluateExpression(*_constant.value(), constantType).commaSeparatedList());
|
||||
templ("code", generator.code());
|
||||
templ("ret", IRVariable("ret", constantType).commaSeparatedList());
|
||||
|
||||
return templ.render();
|
||||
});
|
||||
}
|
||||
|
||||
void IRGeneratorForStatements::endVisit(VariableDeclarationStatement const& _varDeclStatement)
|
||||
{
|
||||
if (Expression const* expression = _varDeclStatement.initialValue())
|
||||
@@ -230,29 +253,53 @@ bool IRGeneratorForStatements::visit(Conditional const& _conditional)
|
||||
bool IRGeneratorForStatements::visit(Assignment const& _assignment)
|
||||
{
|
||||
_assignment.rightHandSide().accept(*this);
|
||||
Type const* intermediateType = type(_assignment.rightHandSide()).closestTemporaryType(
|
||||
&type(_assignment.leftHandSide())
|
||||
);
|
||||
IRVariable value = convert(_assignment.rightHandSide(), *intermediateType);
|
||||
|
||||
Token assignmentOperator = _assignment.assignmentOperator();
|
||||
Token binaryOperator =
|
||||
assignmentOperator == Token::Assign ?
|
||||
assignmentOperator :
|
||||
TokenTraits::AssignmentToBinaryOp(assignmentOperator);
|
||||
|
||||
Type const* rightIntermediateType =
|
||||
TokenTraits::isShiftOp(binaryOperator) ?
|
||||
type(_assignment.rightHandSide()).mobileType() :
|
||||
type(_assignment.rightHandSide()).closestTemporaryType(
|
||||
&type(_assignment.leftHandSide())
|
||||
);
|
||||
solAssert(rightIntermediateType, "");
|
||||
IRVariable value = convert(_assignment.rightHandSide(), *rightIntermediateType);
|
||||
_assignment.leftHandSide().accept(*this);
|
||||
solAssert(!!m_currentLValue, "LValue not retrieved.");
|
||||
|
||||
if (_assignment.assignmentOperator() != Token::Assign)
|
||||
if (assignmentOperator != Token::Assign)
|
||||
{
|
||||
solAssert(type(_assignment.leftHandSide()) == *intermediateType, "");
|
||||
solAssert(intermediateType->isValueType(), "Compound operators only available for value types.");
|
||||
|
||||
solAssert(type(_assignment.leftHandSide()).isValueType(), "Compound operators only available for value types.");
|
||||
solAssert(rightIntermediateType->isValueType(), "Compound operators only available for value types.");
|
||||
IRVariable leftIntermediate = readFromLValue(*m_currentLValue);
|
||||
m_code << value.name() << " := " << binaryOperation(
|
||||
TokenTraits::AssignmentToBinaryOp(_assignment.assignmentOperator()),
|
||||
*intermediateType,
|
||||
leftIntermediate.name(),
|
||||
value.name()
|
||||
);
|
||||
if (TokenTraits::isShiftOp(binaryOperator))
|
||||
{
|
||||
solAssert(type(_assignment) == leftIntermediate.type(), "");
|
||||
solAssert(type(_assignment) == type(_assignment.leftHandSide()), "");
|
||||
define(_assignment) << shiftOperation(binaryOperator, leftIntermediate, value);
|
||||
|
||||
writeToLValue(*m_currentLValue, IRVariable(_assignment));
|
||||
m_currentLValue.reset();
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
solAssert(type(_assignment.leftHandSide()) == *rightIntermediateType, "");
|
||||
m_code << value.name() << " := " << binaryOperation(
|
||||
binaryOperator,
|
||||
*rightIntermediateType,
|
||||
leftIntermediate.name(),
|
||||
value.name()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
writeToLValue(*m_currentLValue, value);
|
||||
|
||||
m_currentLValue.reset();
|
||||
if (*_assignment.annotation().type != *TypeProvider::emptyTuple())
|
||||
define(_assignment, value);
|
||||
@@ -477,12 +524,16 @@ bool IRGeneratorForStatements::visit(BinaryOperation const& _binOp)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (commonType->category() == Type::Category::RationalNumber)
|
||||
{
|
||||
define(_binOp) << toCompactHexWithPrefix(commonType->literalValue(nullptr)) << "\n";
|
||||
return false; // skip sub-expressions
|
||||
}
|
||||
|
||||
_binOp.leftExpression().accept(*this);
|
||||
_binOp.rightExpression().accept(*this);
|
||||
|
||||
if (commonType->category() == Type::Category::RationalNumber)
|
||||
define(_binOp) << toCompactHexWithPrefix(commonType->literalValue(nullptr)) << "\n";
|
||||
else if (TokenTraits::isCompareOp(op))
|
||||
if (TokenTraits::isCompareOp(op))
|
||||
{
|
||||
if (auto type = dynamic_cast<FunctionType const*>(commonType))
|
||||
{
|
||||
@@ -496,9 +547,9 @@ bool IRGeneratorForStatements::visit(BinaryOperation const& _binOp)
|
||||
isSigned = type->isSigned();
|
||||
|
||||
string args =
|
||||
expressionAsType(_binOp.leftExpression(), *commonType) +
|
||||
expressionAsType(_binOp.leftExpression(), *commonType, true) +
|
||||
", " +
|
||||
expressionAsType(_binOp.rightExpression(), *commonType);
|
||||
expressionAsType(_binOp.rightExpression(), *commonType, true);
|
||||
|
||||
string expr;
|
||||
if (op == Token::Equal)
|
||||
@@ -517,6 +568,12 @@ bool IRGeneratorForStatements::visit(BinaryOperation const& _binOp)
|
||||
solAssert(false, "Unknown comparison operator.");
|
||||
define(_binOp) << expr << "\n";
|
||||
}
|
||||
else if (TokenTraits::isShiftOp(op))
|
||||
{
|
||||
IRVariable left = convert(_binOp.leftExpression(), *commonType);
|
||||
IRVariable right = convert(_binOp.rightExpression(), *type(_binOp.rightExpression()).mobileType());
|
||||
define(_binOp) << shiftOperation(_binOp.getOperator(), left, right) << "\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
string left = expressionAsType(_binOp.leftExpression(), *commonType);
|
||||
@@ -584,13 +641,6 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
||||
break;
|
||||
case FunctionType::Kind::Internal:
|
||||
{
|
||||
vector<string> args;
|
||||
for (size_t i = 0; i < arguments.size(); ++i)
|
||||
if (functionType->takesArbitraryParameters())
|
||||
args.emplace_back(IRVariable(*arguments[i]).commaSeparatedList());
|
||||
else
|
||||
args.emplace_back(convert(*arguments[i], *parameterTypes[i]).commaSeparatedList());
|
||||
|
||||
optional<FunctionDefinition const*> functionDef;
|
||||
if (auto memberAccess = dynamic_cast<MemberAccess const*>(&_functionCall.expression()))
|
||||
{
|
||||
@@ -628,6 +678,13 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
||||
solAssert(functionDef.has_value(), "");
|
||||
solAssert(functionDef.value() == nullptr || functionDef.value()->isImplemented(), "");
|
||||
|
||||
vector<string> args;
|
||||
for (size_t i = 0; i < arguments.size(); ++i)
|
||||
if (functionType->takesArbitraryParameters())
|
||||
args += IRVariable(*arguments[i]).stackSlots();
|
||||
else
|
||||
args += convert(*arguments[i], *parameterTypes[i]).stackSlots();
|
||||
|
||||
if (functionDef.value() != nullptr)
|
||||
define(_functionCall) <<
|
||||
m_context.enqueueFunctionForCodeGeneration(*functionDef.value()) <<
|
||||
@@ -649,10 +706,12 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
||||
}
|
||||
case FunctionType::Kind::External:
|
||||
case FunctionType::Kind::DelegateCall:
|
||||
appendExternalFunctionCall(_functionCall, arguments);
|
||||
break;
|
||||
case FunctionType::Kind::BareCall:
|
||||
case FunctionType::Kind::BareDelegateCall:
|
||||
case FunctionType::Kind::BareStaticCall:
|
||||
appendExternalFunctionCall(_functionCall, arguments);
|
||||
appendBareCall(_functionCall, arguments);
|
||||
break;
|
||||
case FunctionType::Kind::BareCallCode:
|
||||
solAssert(false, "Callcode has been removed.");
|
||||
@@ -663,7 +722,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
||||
ABIFunctions abi(m_context.evmVersion(), m_context.revertStrings(), m_context.functionCollector());
|
||||
|
||||
vector<IRVariable> indexedArgs;
|
||||
string nonIndexedArgs;
|
||||
vector<string> nonIndexedArgs;
|
||||
TypePointers nonIndexedArgTypes;
|
||||
TypePointers nonIndexedParamTypes;
|
||||
if (!event.isAnonymous())
|
||||
@@ -686,10 +745,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
||||
}
|
||||
else
|
||||
{
|
||||
string vars = IRVariable(arg).commaSeparatedList();
|
||||
if (!vars.empty())
|
||||
// In reverse because abi_encode expects it like that.
|
||||
nonIndexedArgs = ", " + move(vars) + nonIndexedArgs;
|
||||
nonIndexedArgs += IRVariable(arg).stackSlots();
|
||||
nonIndexedArgTypes.push_back(arg.annotation().type);
|
||||
nonIndexedParamTypes.push_back(paramTypes[i]);
|
||||
}
|
||||
@@ -704,7 +760,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
||||
templ("end", m_context.newYulVariable());
|
||||
templ("freeMemory", freeMemory());
|
||||
templ("encode", abi.tupleEncoder(nonIndexedArgTypes, nonIndexedParamTypes));
|
||||
templ("nonIndexedArgs", nonIndexedArgs);
|
||||
templ("nonIndexedArgs", joinHumanReadablePrefixed(nonIndexedArgs));
|
||||
templ("log", "log" + to_string(indexedArgs.size()));
|
||||
templ("indexedArgs", joinHumanReadablePrefixed(indexedArgs | boost::adaptors::transformed([&](auto const& _arg) {
|
||||
return _arg.commaSeparatedList();
|
||||
@@ -731,13 +787,134 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
||||
|
||||
break;
|
||||
}
|
||||
case FunctionType::Kind::ABIEncode:
|
||||
case FunctionType::Kind::ABIEncodePacked:
|
||||
case FunctionType::Kind::ABIEncodeWithSelector:
|
||||
case FunctionType::Kind::ABIEncodeWithSignature:
|
||||
{
|
||||
bool const isPacked = functionType->kind() == FunctionType::Kind::ABIEncodePacked;
|
||||
solAssert(functionType->padArguments() != isPacked, "");
|
||||
bool const hasSelectorOrSignature =
|
||||
functionType->kind() == FunctionType::Kind::ABIEncodeWithSelector ||
|
||||
functionType->kind() == FunctionType::Kind::ABIEncodeWithSignature;
|
||||
|
||||
TypePointers argumentTypes;
|
||||
TypePointers targetTypes;
|
||||
vector<string> argumentVars;
|
||||
for (size_t i = 0; i < arguments.size(); ++i)
|
||||
{
|
||||
// ignore selector
|
||||
if (hasSelectorOrSignature && i == 0)
|
||||
continue;
|
||||
argumentTypes.emplace_back(&type(*arguments[i]));
|
||||
targetTypes.emplace_back(type(*arguments[i]).fullEncodingType(false, true, isPacked));
|
||||
argumentVars += IRVariable(*arguments[i]).stackSlots();
|
||||
}
|
||||
|
||||
string selector;
|
||||
if (functionType->kind() == FunctionType::Kind::ABIEncodeWithSignature)
|
||||
{
|
||||
// hash the signature
|
||||
Type const& selectorType = type(*arguments.front());
|
||||
if (auto const* stringType = dynamic_cast<StringLiteralType const*>(&selectorType))
|
||||
{
|
||||
FixedHash<4> hash(keccak256(stringType->value()));
|
||||
selector = formatNumber(u256(FixedHash<4>::Arith(hash)) << (256 - 32));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Used to reset the free memory pointer later.
|
||||
string freeMemoryPre = m_context.newYulVariable();
|
||||
m_code << "let " << freeMemoryPre << " := " << freeMemory() << "\n";
|
||||
IRVariable array = convert(*arguments[0], *TypeProvider::bytesMemory());
|
||||
IRVariable hashVariable(m_context.newYulVariable(), *TypeProvider::fixedBytes(32));
|
||||
|
||||
define(hashVariable) <<
|
||||
"keccak256(" <<
|
||||
m_utils.arrayDataAreaFunction(*TypeProvider::bytesMemory()) <<
|
||||
"(" <<
|
||||
array.commaSeparatedList() <<
|
||||
"), " <<
|
||||
m_utils.arrayLengthFunction(*TypeProvider::bytesMemory()) <<
|
||||
"(" <<
|
||||
array.commaSeparatedList() <<
|
||||
"))\n";
|
||||
IRVariable selectorVariable(m_context.newYulVariable(), *TypeProvider::fixedBytes(4));
|
||||
define(selectorVariable, hashVariable);
|
||||
m_code << "mstore(" << to_string(CompilerUtils::freeMemoryPointer) << ", " << freeMemoryPre << ")\n";
|
||||
}
|
||||
}
|
||||
else if (functionType->kind() == FunctionType::Kind::ABIEncodeWithSelector)
|
||||
selector = convert(*arguments.front(), *TypeProvider::fixedBytes(4)).name();
|
||||
|
||||
Whiskers templ(R"(
|
||||
let <data> := <allocateTemporary>()
|
||||
let <mpos> := add(<data>, 0x20)
|
||||
<?+selector>
|
||||
mstore(<mpos>, <selector>)
|
||||
<mpos> := add(<mpos>, 4)
|
||||
</+selector>
|
||||
let <mend> := <encode>(<mpos><arguments>)
|
||||
mstore(<data>, sub(<mend>, add(<data>, 0x20)))
|
||||
mstore(<freeMemPtr>, <roundUp>(<mend>))
|
||||
)");
|
||||
templ("data", IRVariable(_functionCall).part("mpos").name());
|
||||
templ("allocateTemporary", m_utils.allocationTemporaryMemoryFunction());
|
||||
templ("mpos", m_context.newYulVariable());
|
||||
templ("mend", m_context.newYulVariable());
|
||||
templ("selector", selector);
|
||||
templ("encode",
|
||||
isPacked ?
|
||||
m_context.abiFunctions().tupleEncoderPacked(argumentTypes, targetTypes) :
|
||||
m_context.abiFunctions().tupleEncoder(argumentTypes, targetTypes, false)
|
||||
);
|
||||
templ("arguments", joinHumanReadablePrefixed(argumentVars));
|
||||
templ("freeMemPtr", to_string(CompilerUtils::freeMemoryPointer));
|
||||
templ("roundUp", m_utils.roundUpFunction());
|
||||
|
||||
m_code << templ.render();
|
||||
break;
|
||||
}
|
||||
case FunctionType::Kind::Revert:
|
||||
{
|
||||
solAssert(arguments.size() == parameterTypes.size(), "");
|
||||
if (arguments.empty())
|
||||
m_code << "revert(0, 0)\n";
|
||||
else
|
||||
solUnimplementedAssert(false, "");
|
||||
{
|
||||
solAssert(arguments.size() == 1, "");
|
||||
|
||||
if (m_context.revertStrings() == RevertStrings::Strip)
|
||||
m_code << "revert(0, 0)\n";
|
||||
else
|
||||
{
|
||||
solAssert(type(*arguments.front()).isImplicitlyConvertibleTo(*TypeProvider::stringMemory()),"");
|
||||
|
||||
Whiskers templ(R"({
|
||||
let <pos> := <allocateTemporary>()
|
||||
mstore(<pos>, <hash>)
|
||||
let <end> := <encode>(add(<pos>, 4) <argumentVars>)
|
||||
revert(<pos>, sub(<end>, <pos>))
|
||||
})");
|
||||
templ("pos", m_context.newYulVariable());
|
||||
templ("end", m_context.newYulVariable());
|
||||
templ(
|
||||
"hash",
|
||||
(u256(util::FixedHash<4>::Arith(util::FixedHash<4>(util::keccak256("Error(string)")))) << (256 - 32)).str()
|
||||
);
|
||||
templ("allocateTemporary", m_utils.allocationTemporaryMemoryFunction());
|
||||
templ(
|
||||
"argumentVars",
|
||||
joinHumanReadablePrefixed(IRVariable{*arguments.front()}.stackSlots())
|
||||
);
|
||||
templ("encode", m_context.abiFunctions().tupleEncoder(
|
||||
{&type(*arguments.front())},
|
||||
{TypeProvider::stringMemory()}
|
||||
));
|
||||
|
||||
m_code << templ.render();
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -760,6 +937,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
||||
solAssert(arguments.size() == 1, "");
|
||||
|
||||
ArrayType const* arrayType = TypeProvider::bytesMemory();
|
||||
|
||||
auto array = convert(*arguments[0], *arrayType);
|
||||
|
||||
define(_functionCall) <<
|
||||
@@ -905,11 +1083,11 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
||||
);
|
||||
|
||||
TypePointers argumentTypes;
|
||||
string constructorParams;
|
||||
vector<string> constructorParams;
|
||||
for (ASTPointer<Expression const> const& arg: arguments)
|
||||
{
|
||||
argumentTypes.push_back(arg->annotation().type);
|
||||
constructorParams += ", " + IRVariable{*arg}.commaSeparatedList();
|
||||
constructorParams += IRVariable{*arg}.stackSlots();
|
||||
}
|
||||
|
||||
ContractDefinition const* contract =
|
||||
@@ -935,9 +1113,9 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
||||
t("releaseTemporaryMemory", m_utils.releaseTemporaryMemoryFunction());
|
||||
t("object", m_context.creationObjectName(*contract));
|
||||
t("abiEncode",
|
||||
m_context.abiFunctions().tupleEncoder(argumentTypes, functionType->parameterTypes(),false)
|
||||
m_context.abiFunctions().tupleEncoder(argumentTypes, functionType->parameterTypes(), false)
|
||||
);
|
||||
t("constructorParams", constructorParams);
|
||||
t("constructorParams", joinHumanReadablePrefixed(constructorParams));
|
||||
t("value", functionType->valueSet() ? IRVariable(_functionCall.expression()).part("value").name() : "0");
|
||||
t("saltSet", functionType->saltSet());
|
||||
if (functionType->saltSet())
|
||||
@@ -1126,7 +1304,20 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess)
|
||||
solAssert(false, "Blockhash has been removed.");
|
||||
else if (member == "creationCode" || member == "runtimeCode")
|
||||
{
|
||||
solUnimplementedAssert(false, "");
|
||||
solUnimplementedAssert(member != "runtimeCode", "");
|
||||
TypePointer arg = dynamic_cast<MagicType const&>(*_memberAccess.expression().annotation().type).typeArgument();
|
||||
ContractDefinition const& contract = dynamic_cast<ContractType const&>(*arg).contractDefinition();
|
||||
m_context.subObjectsCreated().insert(&contract);
|
||||
m_code << Whiskers(R"(
|
||||
let <size> := datasize("<objectName>")
|
||||
let <result> := <allocationFunction>(add(<size>, 32))
|
||||
mstore(<result>, <size>)
|
||||
datacopy(add(<result>, 32), dataoffset("<objectName>"), <size>)
|
||||
)")
|
||||
("allocationFunction", m_utils.allocationFunction())
|
||||
("size", m_context.newYulVariable())
|
||||
("objectName", m_context.creationObjectName(contract))
|
||||
("result", IRVariable(_memberAccess).commaSeparatedList()).render();
|
||||
}
|
||||
else if (member == "name")
|
||||
{
|
||||
@@ -1141,6 +1332,16 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess)
|
||||
result ^= fromBigEndian<uint64_t>(function.first.ref());
|
||||
define(_memberAccess) << formatNumber(u256{result} << (256 - 32)) << "\n";
|
||||
}
|
||||
else if (member == "min" || member == "max")
|
||||
{
|
||||
MagicType const* arg = dynamic_cast<MagicType const*>(_memberAccess.expression().annotation().type);
|
||||
IntegerType const* integerType = dynamic_cast<IntegerType const*>(arg->typeArgument());
|
||||
|
||||
if (member == "min")
|
||||
define(_memberAccess) << formatNumber(integerType->min()) << "\n";
|
||||
else
|
||||
define(_memberAccess) << formatNumber(integerType->max()) << "\n";
|
||||
}
|
||||
else if (set<string>{"encode", "encodePacked", "encodeWithSelector", "encodeWithSignature", "decode"}.count(member))
|
||||
{
|
||||
// no-op
|
||||
@@ -1312,14 +1513,11 @@ void IRGeneratorForStatements::endVisit(IndexAccess const& _indexAccess)
|
||||
solAssert(keyType.sizeOnStack() <= 1, "");
|
||||
|
||||
string slot = m_context.newYulVariable();
|
||||
Whiskers templ("let <slot> := <indexAccess>(<base> <key>)\n");
|
||||
Whiskers templ("let <slot> := <indexAccess>(<base><?+key>,<key></+key>)\n");
|
||||
templ("slot", slot);
|
||||
templ("indexAccess", m_utils.mappingIndexAccessFunction(mappingType, keyType));
|
||||
templ("base", IRVariable(_indexAccess.baseExpression()).commaSeparatedList());
|
||||
if (keyType.sizeOnStack() == 0)
|
||||
templ("key", "");
|
||||
else
|
||||
templ("key", ", " + IRVariable(*_indexAccess.indexExpression()).commaSeparatedList());
|
||||
templ("key", IRVariable(*_indexAccess.indexExpression()).commaSeparatedList());
|
||||
m_code << templ.render();
|
||||
setLValue(_indexAccess, IRLValue{
|
||||
*_indexAccess.annotation().type,
|
||||
@@ -1542,11 +1740,9 @@ void IRGeneratorForStatements::handleVariableReference(
|
||||
Expression const& _referencingExpression
|
||||
)
|
||||
{
|
||||
// TODO for the constant case, we have to be careful:
|
||||
// If the value is visited twice, `defineExpression` is called twice on
|
||||
// the same expression.
|
||||
solUnimplementedAssert(!_variable.isConstant(), "");
|
||||
if (_variable.isStateVariable() && _variable.immutable())
|
||||
if (_variable.isStateVariable() && _variable.isConstant())
|
||||
define(_referencingExpression) << constantValueFunction(_variable) << "()\n";
|
||||
else if (_variable.isStateVariable() && _variable.immutable())
|
||||
setLValue(_referencingExpression, IRLValue{
|
||||
*_variable.annotation().type,
|
||||
IRLValue::Immutable{&_variable}
|
||||
@@ -1574,18 +1770,20 @@ void IRGeneratorForStatements::appendExternalFunctionCall(
|
||||
)
|
||||
{
|
||||
FunctionType const& funType = dynamic_cast<FunctionType const&>(type(_functionCall.expression()));
|
||||
solAssert(
|
||||
funType.takesArbitraryParameters() ||
|
||||
_arguments.size() == funType.parameterTypes().size(), ""
|
||||
);
|
||||
solUnimplementedAssert(!funType.bound(), "");
|
||||
solAssert(!funType.takesArbitraryParameters(), "");
|
||||
solAssert(_arguments.size() == funType.parameterTypes().size(), "");
|
||||
solAssert(!funType.isBareCall(), "");
|
||||
FunctionType::Kind const funKind = funType.kind();
|
||||
|
||||
solAssert(funKind != FunctionType::Kind::BareStaticCall || m_context.evmVersion().hasStaticCall(), "");
|
||||
solAssert(funKind != FunctionType::Kind::BareCallCode, "Callcode has been removed.");
|
||||
solAssert(
|
||||
funKind == FunctionType::Kind::External || funKind == FunctionType::Kind::DelegateCall,
|
||||
"Can only be used for regular external calls."
|
||||
);
|
||||
|
||||
bool const isDelegateCall = funKind == FunctionType::Kind::BareDelegateCall || funKind == FunctionType::Kind::DelegateCall;
|
||||
bool const useStaticCall = funKind == FunctionType::Kind::BareStaticCall || (funType.stateMutability() <= StateMutability::View && m_context.evmVersion().hasStaticCall());
|
||||
solUnimplementedAssert(!funType.bound(), "");
|
||||
|
||||
bool const isDelegateCall = funKind == FunctionType::Kind::DelegateCall;
|
||||
bool const useStaticCall = funType.stateMutability() <= StateMutability::View && m_context.evmVersion().hasStaticCall();
|
||||
|
||||
ReturnInfo const returnInfo{m_context.evmVersion(), funType};
|
||||
|
||||
@@ -1594,12 +1792,9 @@ void IRGeneratorForStatements::appendExternalFunctionCall(
|
||||
for (auto const& arg: _arguments)
|
||||
{
|
||||
argumentTypes.emplace_back(&type(*arg));
|
||||
if (IRVariable(*arg).type().sizeOnStack() > 0)
|
||||
argumentStrings.emplace_back(IRVariable(*arg).commaSeparatedList());
|
||||
argumentStrings += IRVariable(*arg).stackSlots();
|
||||
}
|
||||
string argumentString = argumentStrings.empty() ? ""s : (", " + joinHumanReadable(argumentStrings));
|
||||
|
||||
solUnimplementedAssert(funKind != FunctionType::Kind::ECRecover, "");
|
||||
|
||||
if (!m_context.evmVersion().canOverchargeGasForCall())
|
||||
{
|
||||
@@ -1611,33 +1806,19 @@ void IRGeneratorForStatements::appendExternalFunctionCall(
|
||||
m_code << "mstore(add(" << freeMemory() << ", " << to_string(returnInfo.estimatedReturnSize) << "), 0)\n";
|
||||
}
|
||||
|
||||
ABIFunctions abi(m_context.evmVersion(), m_context.revertStrings(), m_context.functionCollector());
|
||||
|
||||
Whiskers templ(R"(
|
||||
<?checkExistence>
|
||||
if iszero(extcodesize(<address>)) { revert(0, 0) }
|
||||
</checkExistence>
|
||||
if iszero(extcodesize(<address>)) { revert(0, 0) }
|
||||
|
||||
// storage for arguments and returned data
|
||||
let <pos> := <freeMemory>
|
||||
<?bareCall>
|
||||
<!bareCall>
|
||||
mstore(<pos>, <shl28>(<funId>))
|
||||
</bareCall>
|
||||
let <end> := <encodeArgs>(
|
||||
<?bareCall>
|
||||
<pos>
|
||||
<!bareCall>
|
||||
add(<pos>, 4)
|
||||
</bareCall>
|
||||
<argumentString>
|
||||
)
|
||||
mstore(<pos>, <shl28>(<funId>))
|
||||
let <end> := <encodeArgs>(add(<pos>, 4) <argumentString>)
|
||||
|
||||
let <success> := <call>(<gas>, <address>, <?hasValue> <value>, </hasValue> <pos>, sub(<end>, <pos>), <pos>, <reservedReturnSize>)
|
||||
<?noTryCall>
|
||||
if iszero(<success>) { <forwardingRevert>() }
|
||||
</noTryCall>
|
||||
<?hasRetVars> let <retVars> </hasRetVars>
|
||||
<?+retVars> let <retVars> </+retVars>
|
||||
if <success> {
|
||||
<?dynamicReturnSize>
|
||||
// copy dynamic return data out
|
||||
@@ -1648,12 +1829,11 @@ void IRGeneratorForStatements::appendExternalFunctionCall(
|
||||
mstore(<freeMemoryPointer>, add(<pos>, <roundUp>(<returnSize>)))
|
||||
|
||||
// decode return parameters from external try-call into retVars
|
||||
<?hasRetVars> <retVars> := </hasRetVars> <abiDecode>(<pos>, add(<pos>, <returnSize>))
|
||||
<?+retVars> <retVars> := </+retVars> <abiDecode>(<pos>, add(<pos>, <returnSize>))
|
||||
}
|
||||
)");
|
||||
templ("pos", m_context.newYulVariable());
|
||||
templ("end", m_context.newYulVariable());
|
||||
templ("bareCall", funType.isBareCall());
|
||||
if (_functionCall.annotation().tryCall)
|
||||
templ("success", m_context.trySuccessConditionVariable(_functionCall));
|
||||
else
|
||||
@@ -1661,17 +1841,8 @@ void IRGeneratorForStatements::appendExternalFunctionCall(
|
||||
templ("freeMemory", freeMemory());
|
||||
templ("shl28", m_utils.shiftLeftFunction(8 * (32 - 4)));
|
||||
|
||||
if (!funType.isBareCall())
|
||||
templ("funId", IRVariable(_functionCall.expression()).part("functionIdentifier").name());
|
||||
|
||||
if (funKind == FunctionType::Kind::ECRecover)
|
||||
templ("address", "1");
|
||||
else if (funKind == FunctionType::Kind::SHA256)
|
||||
templ("address", "2");
|
||||
else if (funKind == FunctionType::Kind::RIPEMD160)
|
||||
templ("address", "3");
|
||||
else
|
||||
templ("address", IRVariable(_functionCall.expression()).part("address").name());
|
||||
templ("funId", IRVariable(_functionCall.expression()).part("functionIdentifier").name());
|
||||
templ("address", IRVariable(_functionCall.expression()).part("address").name());
|
||||
|
||||
// Always use the actual return length, and not our calculated expected length, if returndatacopy is supported.
|
||||
// This ensures it can catch badly formatted input from external calls.
|
||||
@@ -1684,39 +1855,20 @@ void IRGeneratorForStatements::appendExternalFunctionCall(
|
||||
|
||||
string const retVars = IRVariable(_functionCall).commaSeparatedList();
|
||||
templ("retVars", retVars);
|
||||
templ("hasRetVars", !retVars.empty());
|
||||
solAssert(retVars.empty() == returnInfo.returnTypes.empty(), "");
|
||||
|
||||
templ("roundUp", m_utils.roundUpFunction());
|
||||
templ("abiDecode", abi.tupleDecoder(returnInfo.returnTypes, true));
|
||||
templ("abiDecode", m_context.abiFunctions().tupleDecoder(returnInfo.returnTypes, true));
|
||||
templ("dynamicReturnSize", returnInfo.dynamicReturnSize);
|
||||
templ("freeMemoryPointer", to_string(CompilerUtils::freeMemoryPointer));
|
||||
|
||||
templ("noTryCall", !_functionCall.annotation().tryCall);
|
||||
|
||||
// If the function takes arbitrary parameters or is a bare call, copy dynamic length data in place.
|
||||
// Move arguments to memory, will not update the free memory pointer (but will update the memory
|
||||
// pointer on the stack).
|
||||
bool encodeInPlace = funType.takesArbitraryParameters() || funType.isBareCall();
|
||||
if (funType.kind() == FunctionType::Kind::ECRecover)
|
||||
// This would be the only combination of padding and in-place encoding,
|
||||
// but all parameters of ecrecover are value types anyway.
|
||||
encodeInPlace = false;
|
||||
bool encodeForLibraryCall = funKind == FunctionType::Kind::DelegateCall;
|
||||
|
||||
solUnimplementedAssert(encodeInPlace == !funType.padArguments(), "");
|
||||
if (encodeInPlace)
|
||||
{
|
||||
solUnimplementedAssert(!encodeForLibraryCall, "");
|
||||
templ("encodeArgs", abi.tupleEncoderPacked(argumentTypes, funType.parameterTypes()));
|
||||
}
|
||||
else
|
||||
templ("encodeArgs", abi.tupleEncoder(argumentTypes, funType.parameterTypes(), encodeForLibraryCall));
|
||||
templ("argumentString", argumentString);
|
||||
|
||||
// Output data will replace input data, unless we have ECRecover (then, output
|
||||
// area will be 32 bytes just before input area).
|
||||
solUnimplementedAssert(funKind != FunctionType::Kind::ECRecover, "");
|
||||
solAssert(funType.padArguments(), "");
|
||||
templ("encodeArgs", m_context.abiFunctions().tupleEncoder(argumentTypes, funType.parameterTypes(), encodeForLibraryCall));
|
||||
templ("argumentString", joinHumanReadablePrefixed(argumentStrings));
|
||||
|
||||
solAssert(!isDelegateCall || !funType.valueSet(), "Value set for delegatecall");
|
||||
solAssert(!useStaticCall || !funType.valueSet(), "Value set for staticcall");
|
||||
@@ -1724,10 +1876,6 @@ void IRGeneratorForStatements::appendExternalFunctionCall(
|
||||
templ("hasValue", !isDelegateCall && !useStaticCall);
|
||||
templ("value", funType.valueSet() ? IRVariable(_functionCall.expression()).part("value").name() : "0");
|
||||
|
||||
// Check that the target contract exists (has code) for non-low-level calls.
|
||||
bool checkExistence = (funKind == FunctionType::Kind::External || funKind == FunctionType::Kind::DelegateCall);
|
||||
templ("checkExistence", checkExistence);
|
||||
|
||||
if (funType.gasSet())
|
||||
templ("gas", IRVariable(_functionCall.expression()).part("gas").name());
|
||||
else if (m_context.evmVersion().canOverchargeGasForCall())
|
||||
@@ -1740,8 +1888,6 @@ void IRGeneratorForStatements::appendExternalFunctionCall(
|
||||
u256 gasNeededByCaller = evmasm::GasCosts::callGas(m_context.evmVersion()) + 10;
|
||||
if (funType.valueSet())
|
||||
gasNeededByCaller += evmasm::GasCosts::callValueTransferGas;
|
||||
if (!checkExistence)
|
||||
gasNeededByCaller += evmasm::GasCosts::callNewAccountGas; // we never know
|
||||
templ("gas", "sub(gas(), " + formatNumber(gasNeededByCaller) + ")");
|
||||
}
|
||||
// Order is important here, STATICCALL might overlap with DELEGATECALL.
|
||||
@@ -1754,8 +1900,103 @@ void IRGeneratorForStatements::appendExternalFunctionCall(
|
||||
|
||||
templ("forwardingRevert", m_utils.forwardingRevertFunction());
|
||||
|
||||
solUnimplementedAssert(funKind != FunctionType::Kind::RIPEMD160, "");
|
||||
solUnimplementedAssert(funKind != FunctionType::Kind::ECRecover, "");
|
||||
m_code << templ.render();
|
||||
}
|
||||
|
||||
void IRGeneratorForStatements::appendBareCall(
|
||||
FunctionCall const& _functionCall,
|
||||
vector<ASTPointer<Expression const>> const& _arguments
|
||||
)
|
||||
{
|
||||
FunctionType const& funType = dynamic_cast<FunctionType const&>(type(_functionCall.expression()));
|
||||
solAssert(
|
||||
!funType.bound() &&
|
||||
!funType.takesArbitraryParameters() &&
|
||||
_arguments.size() == 1 &&
|
||||
funType.parameterTypes().size() == 1, ""
|
||||
);
|
||||
FunctionType::Kind const funKind = funType.kind();
|
||||
|
||||
solAssert(funKind != FunctionType::Kind::BareStaticCall || m_context.evmVersion().hasStaticCall(), "");
|
||||
solAssert(funKind != FunctionType::Kind::BareCallCode, "Callcode has been removed.");
|
||||
solAssert(
|
||||
funKind == FunctionType::Kind::BareCall ||
|
||||
funKind == FunctionType::Kind::BareDelegateCall ||
|
||||
funKind == FunctionType::Kind::BareStaticCall, ""
|
||||
);
|
||||
|
||||
solAssert(!_functionCall.annotation().tryCall, "");
|
||||
Whiskers templ(R"(
|
||||
<?needsEncoding>
|
||||
let <pos> := mload(<freeMemoryPointer>)
|
||||
let <length> := sub(<encode>(<pos> <?+arg>,</+arg> <arg>), <pos>)
|
||||
<!needsEncoding>
|
||||
let <pos> := add(<arg>, 0x20)
|
||||
let <length> := mload(<arg>)
|
||||
</needsEncoding>
|
||||
|
||||
let <success> := <call>(<gas>, <address>, <?+value> <value>, </+value> <pos>, <length>, 0, 0)
|
||||
<?+returndataVar>
|
||||
let <returndataVar> := <extractReturndataFunction>()
|
||||
</+returndataVar>
|
||||
)");
|
||||
|
||||
templ("freeMemoryPointer", to_string(CompilerUtils::freeMemoryPointer));
|
||||
templ("pos", m_context.newYulVariable());
|
||||
templ("length", m_context.newYulVariable());
|
||||
|
||||
templ("arg", IRVariable(*_arguments.front()).commaSeparatedList());
|
||||
Type const& argType = type(*_arguments.front());
|
||||
if (argType == *TypeProvider::bytesMemory() || argType == *TypeProvider::stringMemory())
|
||||
templ("needsEncoding", false);
|
||||
else
|
||||
{
|
||||
templ("needsEncoding", true);
|
||||
ABIFunctions abi(m_context.evmVersion(), m_context.revertStrings(), m_context.functionCollector());
|
||||
templ("encode", abi.tupleEncoderPacked({&argType}, {TypeProvider::bytesMemory()}));
|
||||
}
|
||||
|
||||
templ("success", IRVariable(_functionCall).tupleComponent(0).name());
|
||||
if (IRVariable(_functionCall).tupleComponent(1).type().category() == Type::Category::InaccessibleDynamic)
|
||||
templ("returndataVar", "");
|
||||
else
|
||||
{
|
||||
templ("returndataVar", IRVariable(_functionCall).tupleComponent(1).part("mpos").name());
|
||||
templ("extractReturndataFunction", m_utils.extractReturndataFunction());
|
||||
}
|
||||
|
||||
templ("address", IRVariable(_functionCall.expression()).part("address").name());
|
||||
|
||||
if (funKind == FunctionType::Kind::BareCall)
|
||||
{
|
||||
templ("value", funType.valueSet() ? IRVariable(_functionCall.expression()).part("value").name() : "0");
|
||||
templ("call", "call");
|
||||
}
|
||||
else
|
||||
{
|
||||
solAssert(!funType.valueSet(), "Value set for delegatecall or staticcall.");
|
||||
templ("value", "");
|
||||
if (funKind == FunctionType::Kind::BareStaticCall)
|
||||
templ("call", "staticcall");
|
||||
else
|
||||
templ("call", "delegatecall");
|
||||
}
|
||||
|
||||
if (funType.gasSet())
|
||||
templ("gas", IRVariable(_functionCall.expression()).part("gas").name());
|
||||
else if (m_context.evmVersion().canOverchargeGasForCall())
|
||||
// Send all gas (requires tangerine whistle EVM)
|
||||
templ("gas", "gas()");
|
||||
else
|
||||
{
|
||||
// send all gas except the amount needed to execute "SUB" and "CALL"
|
||||
// @todo this retains too much gas for now, needs to be fine-tuned.
|
||||
u256 gasNeededByCaller = evmasm::GasCosts::callGas(m_context.evmVersion()) + 10;
|
||||
if (funType.valueSet())
|
||||
gasNeededByCaller += evmasm::GasCosts::callValueTransferGas;
|
||||
gasNeededByCaller += evmasm::GasCosts::callNewAccountGas; // we never know
|
||||
templ("gas", "sub(gas(), " + formatNumber(gasNeededByCaller) + ")");
|
||||
}
|
||||
|
||||
m_code << templ.render();
|
||||
}
|
||||
@@ -1777,11 +2018,16 @@ IRVariable IRGeneratorForStatements::convert(IRVariable const& _from, Type const
|
||||
}
|
||||
}
|
||||
|
||||
std::string IRGeneratorForStatements::expressionAsType(Expression const& _expression, Type const& _to)
|
||||
std::string IRGeneratorForStatements::expressionAsType(Expression const& _expression, Type const& _to, bool _forceCleanup)
|
||||
{
|
||||
IRVariable from(_expression);
|
||||
if (from.type() == _to)
|
||||
return from.commaSeparatedList();
|
||||
{
|
||||
if (_forceCleanup)
|
||||
return m_utils.cleanupFunction(_to) + "(" + from.commaSeparatedList() + ")";
|
||||
else
|
||||
return from.commaSeparatedList();
|
||||
}
|
||||
else
|
||||
return m_utils.conversionFunction(from.type(), _to) + "(" + from.commaSeparatedList() + ")";
|
||||
}
|
||||
@@ -1860,6 +2106,10 @@ string IRGeneratorForStatements::binaryOperation(
|
||||
string const& _right
|
||||
)
|
||||
{
|
||||
solAssert(
|
||||
!TokenTraits::isShiftOp(_operator),
|
||||
"Have to use specific shift operation function for shifts."
|
||||
);
|
||||
if (IntegerType const* type = dynamic_cast<IntegerType const*>(&_type))
|
||||
{
|
||||
string fun;
|
||||
@@ -1903,6 +2153,31 @@ string IRGeneratorForStatements::binaryOperation(
|
||||
return {};
|
||||
}
|
||||
|
||||
std::string IRGeneratorForStatements::shiftOperation(
|
||||
langutil::Token _operator,
|
||||
IRVariable const& _value,
|
||||
IRVariable const& _amountToShift
|
||||
)
|
||||
{
|
||||
IntegerType const* amountType = dynamic_cast<IntegerType const*>(&_amountToShift.type());
|
||||
solAssert(amountType, "");
|
||||
|
||||
solAssert(_operator == Token::SHL || _operator == Token::SAR, "");
|
||||
|
||||
return
|
||||
Whiskers(R"(
|
||||
<shift>(<value>, <amount>)
|
||||
)")
|
||||
("shift",
|
||||
_operator == Token::SHL ?
|
||||
m_utils.typedShiftLeftFunction(_value.type(), *amountType) :
|
||||
m_utils.typedShiftRightFunction(_value.type(), *amountType)
|
||||
)
|
||||
("value", _value.name())
|
||||
("amount", _amountToShift.name())
|
||||
.render();
|
||||
}
|
||||
|
||||
void IRGeneratorForStatements::appendAndOrOperatorCode(BinaryOperation const& _binOp)
|
||||
{
|
||||
langutil::Token const op = _binOp.getOperator();
|
||||
@@ -2021,13 +2296,7 @@ IRVariable IRGeneratorForStatements::readFromLValue(IRLValue const& _lvalue)
|
||||
")\n";
|
||||
},
|
||||
[&](IRLValue::Memory const& _memory) {
|
||||
if (_memory.byteArrayElement)
|
||||
define(result) <<
|
||||
m_utils.cleanupFunction(_lvalue.type) <<
|
||||
"(mload(" <<
|
||||
_memory.address <<
|
||||
"))\n";
|
||||
else if (_lvalue.type.isValueType())
|
||||
if (_lvalue.type.isValueType())
|
||||
define(result) <<
|
||||
m_utils.readFromMemory(_lvalue.type) <<
|
||||
"(" <<
|
||||
|
||||
@@ -54,6 +54,10 @@ public:
|
||||
/// Calculates expression's value and returns variable where it was stored
|
||||
IRVariable evaluateExpression(Expression const& _expression, Type const& _to);
|
||||
|
||||
/// @returns the name of a function that computes the value of the given constant
|
||||
/// and also generates the function.
|
||||
std::string constantValueFunction(VariableDeclaration const& _constant);
|
||||
|
||||
void endVisit(VariableDeclarationStatement const& _variableDeclaration) override;
|
||||
bool visit(Conditional const& _conditional) override;
|
||||
bool visit(Assignment const& _assignment) override;
|
||||
@@ -102,6 +106,13 @@ private:
|
||||
std::vector<ASTPointer<Expression const>> const& _arguments
|
||||
);
|
||||
|
||||
/// Appends code for .call / .delegatecall / .staticcall.
|
||||
/// All involved expressions have already been visited.
|
||||
void appendBareCall(
|
||||
FunctionCall const& _functionCall,
|
||||
std::vector<ASTPointer<Expression const>> const& _arguments
|
||||
);
|
||||
|
||||
/// @returns code that evaluates to the first unused memory slot (which does not have to
|
||||
/// be empty).
|
||||
static std::string freeMemory();
|
||||
@@ -112,7 +123,8 @@ private:
|
||||
|
||||
/// @returns a Yul expression representing the current value of @a _expression,
|
||||
/// converted to type @a _to if it does not yet have that type.
|
||||
std::string expressionAsType(Expression const& _expression, Type const& _to);
|
||||
/// If @a _forceCleanup is set to true, it also cleans the value, in case it already has type @a _to.
|
||||
std::string expressionAsType(Expression const& _expression, Type const& _to, bool _forceCleanup = false);
|
||||
|
||||
/// @returns an output stream that can be used to define @a _var using a function call or
|
||||
/// single stack slot expression.
|
||||
@@ -142,6 +154,11 @@ private:
|
||||
std::string const& _right
|
||||
);
|
||||
|
||||
/// @returns code to perform the given shift operation.
|
||||
/// The operation itself will be performed in the type of the value,
|
||||
/// while the amount to shift can have its own type.
|
||||
std::string shiftOperation(langutil::Token _op, IRVariable const& _value, IRVariable const& _shiftAmount);
|
||||
|
||||
/// Assigns the value of @a _value to the lvalue @a _lvalue.
|
||||
void writeToLValue(IRLValue const& _lvalue, IRVariable const& _value);
|
||||
/// @returns a fresh IR variable containing the value of the lvalue @a _lvalue.
|
||||
|
||||
@@ -54,7 +54,7 @@ IRVariable IRVariable::part(string const& _name) const
|
||||
solAssert(itemName.empty() || itemType, "");
|
||||
return IRVariable{suffixedName(itemName), itemType ? *itemType : m_type};
|
||||
}
|
||||
solAssert(false, "Invalid stack item name.");
|
||||
solAssert(false, "Invalid stack item name: " + _name);
|
||||
}
|
||||
|
||||
vector<string> IRVariable::stackSlots() const
|
||||
@@ -89,7 +89,7 @@ string IRVariable::name() const
|
||||
{
|
||||
solAssert(m_type.sizeOnStack() == 1, "");
|
||||
auto const& [itemName, type] = m_type.stackItems().front();
|
||||
solAssert(!type, "");
|
||||
solAssert(!type, "Expected null type for name " + itemName);
|
||||
return suffixedName(itemName);
|
||||
}
|
||||
|
||||
|
||||
@@ -69,10 +69,11 @@ public:
|
||||
/// The returned IRVariable is itself typed with the type of the stack slot as defined
|
||||
/// in ``m_type.stackItems()`` and may again occupy multiple stack slots.
|
||||
IRVariable part(std::string const& _slot) const;
|
||||
private:
|
||||
|
||||
/// @returns a vector containing the names of the stack slots of the variable.
|
||||
std::vector<std::string> stackSlots() const;
|
||||
|
||||
private:
|
||||
/// @returns a name consisting of the base name appended with an underscore and @æ _suffix,
|
||||
/// unless @a _suffix is empty, in which case the base name itself is returned.
|
||||
std::string suffixedName(std::string const& _suffix) const;
|
||||
|
||||
+35
-22
@@ -21,8 +21,6 @@
|
||||
#include <libsolidity/formal/SymbolicState.h>
|
||||
#include <libsolidity/formal/SymbolicTypes.h>
|
||||
|
||||
#include <boost/algorithm/string/replace.hpp>
|
||||
|
||||
using namespace std;
|
||||
using namespace solidity;
|
||||
using namespace solidity::util;
|
||||
@@ -44,6 +42,7 @@ BMC::BMC(
|
||||
if (_enabledSolvers.some())
|
||||
if (!_smtlib2Responses.empty())
|
||||
m_errorReporter.warning(
|
||||
5622_error,
|
||||
"SMT-LIB2 query responses were given in the auxiliary input, "
|
||||
"but this Solidity binary uses an SMT solver (Z3/CVC4) directly."
|
||||
"These responses will be ignored."
|
||||
@@ -74,6 +73,7 @@ void BMC::analyze(SourceUnit const& _source, set<Expression const*> _safeAsserti
|
||||
{
|
||||
m_noSolverWarning = true;
|
||||
m_outerErrorReporter.warning(
|
||||
8084_error,
|
||||
SourceLocation(),
|
||||
"BMC analysis was not possible since no integrated SMT solver (Z3 or CVC4) was found."
|
||||
);
|
||||
@@ -467,6 +467,7 @@ void BMC::internalOrExternalFunctionCall(FunctionCall const& _funCall)
|
||||
inlineFunctionCall(_funCall);
|
||||
else if (funType.kind() == FunctionType::Kind::Internal)
|
||||
m_errorReporter.warning(
|
||||
5729_error,
|
||||
_funCall.location(),
|
||||
"Assertion checker does not yet implement this type of function call."
|
||||
);
|
||||
@@ -591,8 +592,7 @@ void BMC::checkConstantCondition(BMCVerificationTarget& _target)
|
||||
*_target.expression,
|
||||
_target.constraints,
|
||||
_target.value,
|
||||
_target.callStack,
|
||||
"Condition is always $VALUE."
|
||||
_target.callStack
|
||||
);
|
||||
}
|
||||
|
||||
@@ -610,6 +610,8 @@ void BMC::checkUnderflow(BMCVerificationTarget& _target, smt::Expression const&
|
||||
_target.callStack,
|
||||
_target.modelExpressions,
|
||||
_target.expression->location(),
|
||||
4144_error,
|
||||
8312_error,
|
||||
"Underflow (resulting value less than " + formatNumberReadable(intType->minValue()) + ")",
|
||||
"<result>",
|
||||
&_target.value
|
||||
@@ -630,6 +632,8 @@ void BMC::checkOverflow(BMCVerificationTarget& _target, smt::Expression const& _
|
||||
_target.callStack,
|
||||
_target.modelExpressions,
|
||||
_target.expression->location(),
|
||||
2661_error,
|
||||
8065_error,
|
||||
"Overflow (resulting value larger than " + formatNumberReadable(intType->maxValue()) + ")",
|
||||
"<result>",
|
||||
&_target.value
|
||||
@@ -644,6 +648,8 @@ void BMC::checkDivByZero(BMCVerificationTarget& _target)
|
||||
_target.callStack,
|
||||
_target.modelExpressions,
|
||||
_target.expression->location(),
|
||||
3046_error,
|
||||
5272_error,
|
||||
"Division by zero",
|
||||
"<result>",
|
||||
&_target.value
|
||||
@@ -658,6 +664,8 @@ void BMC::checkBalance(BMCVerificationTarget& _target)
|
||||
_target.callStack,
|
||||
_target.modelExpressions,
|
||||
_target.expression->location(),
|
||||
1236_error,
|
||||
4010_error,
|
||||
"Insufficient funds",
|
||||
"address(this).balance"
|
||||
);
|
||||
@@ -672,6 +680,8 @@ void BMC::checkAssert(BMCVerificationTarget& _target)
|
||||
_target.callStack,
|
||||
_target.modelExpressions,
|
||||
_target.expression->location(),
|
||||
4661_error,
|
||||
7812_error,
|
||||
"Assertion violation"
|
||||
);
|
||||
}
|
||||
@@ -702,9 +712,11 @@ void BMC::addVerificationTarget(
|
||||
|
||||
void BMC::checkCondition(
|
||||
smt::Expression _condition,
|
||||
vector<SMTEncoder::CallStackEntry> const& callStack,
|
||||
vector<SMTEncoder::CallStackEntry> const& _callStack,
|
||||
pair<vector<smt::Expression>, vector<string>> const& _modelExpressions,
|
||||
SourceLocation const& _location,
|
||||
ErrorId _errorHappens,
|
||||
ErrorId _errorMightHappen,
|
||||
string const& _description,
|
||||
string const& _additionalValueName,
|
||||
smt::Expression const* _additionalValue
|
||||
@@ -716,7 +728,7 @@ void BMC::checkCondition(
|
||||
vector<smt::Expression> expressionsToEvaluate;
|
||||
vector<string> expressionNames;
|
||||
tie(expressionsToEvaluate, expressionNames) = _modelExpressions;
|
||||
if (callStack.size())
|
||||
if (_callStack.size())
|
||||
if (_additionalValue)
|
||||
{
|
||||
expressionsToEvaluate.emplace_back(*_additionalValue);
|
||||
@@ -747,7 +759,7 @@ void BMC::checkCondition(
|
||||
{
|
||||
std::ostringstream message;
|
||||
message << _description << " happens here";
|
||||
if (callStack.size())
|
||||
if (_callStack.size())
|
||||
{
|
||||
std::ostringstream modelMessage;
|
||||
modelMessage << " for:\n";
|
||||
@@ -760,30 +772,31 @@ void BMC::checkCondition(
|
||||
for (auto const& eval: sortedModel)
|
||||
modelMessage << " " << eval.first << " = " << eval.second << "\n";
|
||||
m_errorReporter.warning(
|
||||
_errorHappens,
|
||||
_location,
|
||||
message.str(),
|
||||
SecondarySourceLocation().append(modelMessage.str(), SourceLocation{})
|
||||
.append(SMTEncoder::callStackMessage(callStack))
|
||||
.append(SMTEncoder::callStackMessage(_callStack))
|
||||
.append(move(secondaryLocation))
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
message << ".";
|
||||
m_errorReporter.warning(_location, message.str(), secondaryLocation);
|
||||
m_errorReporter.warning(6084_error, _location, message.str(), secondaryLocation);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case smt::CheckResult::UNSATISFIABLE:
|
||||
break;
|
||||
case smt::CheckResult::UNKNOWN:
|
||||
m_errorReporter.warning(_location, _description + " might happen here.", secondaryLocation);
|
||||
m_errorReporter.warning(_errorMightHappen, _location, _description + " might happen here.", secondaryLocation);
|
||||
break;
|
||||
case smt::CheckResult::CONFLICTING:
|
||||
m_errorReporter.warning(_location, "At least two SMT solvers provided conflicting answers. Results might not be sound.");
|
||||
m_errorReporter.warning(1584_error, _location, "At least two SMT solvers provided conflicting answers. Results might not be sound.");
|
||||
break;
|
||||
case smt::CheckResult::ERROR:
|
||||
m_errorReporter.warning(_location, "Error trying to invoke SMT solver.");
|
||||
m_errorReporter.warning(1823_error, _location, "Error trying to invoke SMT solver.");
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -794,8 +807,7 @@ void BMC::checkBooleanNotConstant(
|
||||
Expression const& _condition,
|
||||
smt::Expression const& _constraints,
|
||||
smt::Expression const& _value,
|
||||
vector<SMTEncoder::CallStackEntry> const& _callStack,
|
||||
string const& _description
|
||||
vector<SMTEncoder::CallStackEntry> const& _callStack
|
||||
)
|
||||
{
|
||||
// Do not check for const-ness if this is a constant.
|
||||
@@ -813,9 +825,9 @@ void BMC::checkBooleanNotConstant(
|
||||
m_interface->pop();
|
||||
|
||||
if (positiveResult == smt::CheckResult::ERROR || negatedResult == smt::CheckResult::ERROR)
|
||||
m_errorReporter.warning(_condition.location(), "Error trying to invoke SMT solver.");
|
||||
m_errorReporter.warning(8592_error, _condition.location(), "Error trying to invoke SMT solver.");
|
||||
else if (positiveResult == smt::CheckResult::CONFLICTING || negatedResult == smt::CheckResult::CONFLICTING)
|
||||
m_errorReporter.warning(_condition.location(), "At least two SMT solvers provided conflicting answers. Results might not be sound.");
|
||||
m_errorReporter.warning(3356_error, _condition.location(), "At least two SMT solvers provided conflicting answers. Results might not be sound.");
|
||||
else if (positiveResult == smt::CheckResult::SATISFIABLE && negatedResult == smt::CheckResult::SATISFIABLE)
|
||||
{
|
||||
// everything fine.
|
||||
@@ -825,24 +837,25 @@ void BMC::checkBooleanNotConstant(
|
||||
// can't do anything.
|
||||
}
|
||||
else if (positiveResult == smt::CheckResult::UNSATISFIABLE && negatedResult == smt::CheckResult::UNSATISFIABLE)
|
||||
m_errorReporter.warning(_condition.location(), "Condition unreachable.", SMTEncoder::callStackMessage(_callStack));
|
||||
m_errorReporter.warning(2512_error, _condition.location(), "Condition unreachable.", SMTEncoder::callStackMessage(_callStack));
|
||||
else
|
||||
{
|
||||
string value;
|
||||
string description;
|
||||
if (positiveResult == smt::CheckResult::SATISFIABLE)
|
||||
{
|
||||
solAssert(negatedResult == smt::CheckResult::UNSATISFIABLE, "");
|
||||
value = "true";
|
||||
description = "Condition is always true.";
|
||||
}
|
||||
else
|
||||
{
|
||||
solAssert(positiveResult == smt::CheckResult::UNSATISFIABLE, "");
|
||||
solAssert(negatedResult == smt::CheckResult::SATISFIABLE, "");
|
||||
value = "false";
|
||||
description = "Condition is always false.";
|
||||
}
|
||||
m_errorReporter.warning(
|
||||
6838_error,
|
||||
_condition.location(),
|
||||
boost::algorithm::replace_all_copy(_description, "$VALUE", value),
|
||||
description,
|
||||
SMTEncoder::callStackMessage(_callStack)
|
||||
);
|
||||
}
|
||||
@@ -862,7 +875,7 @@ BMC::checkSatisfiableAndGenerateModel(vector<smt::Expression> const& _expression
|
||||
string description("Error querying SMT solver");
|
||||
if (_e.comment())
|
||||
description += ": " + *_e.comment();
|
||||
m_errorReporter.warning(description);
|
||||
m_errorReporter.warning(8140_error, description);
|
||||
result = smt::CheckResult::ERROR;
|
||||
}
|
||||
|
||||
|
||||
@@ -44,6 +44,7 @@ using solidity::util::h256;
|
||||
namespace solidity::langutil
|
||||
{
|
||||
class ErrorReporter;
|
||||
struct ErrorId;
|
||||
struct SourceLocation;
|
||||
}
|
||||
|
||||
@@ -144,22 +145,22 @@ private:
|
||||
/// Check that a condition can be satisfied.
|
||||
void checkCondition(
|
||||
smt::Expression _condition,
|
||||
std::vector<CallStackEntry> const& callStack,
|
||||
std::vector<CallStackEntry> const& _callStack,
|
||||
std::pair<std::vector<smt::Expression>, std::vector<std::string>> const& _modelExpressions,
|
||||
langutil::SourceLocation const& _location,
|
||||
langutil::ErrorId _errorHappens,
|
||||
langutil::ErrorId _errorMightHappen,
|
||||
std::string const& _description,
|
||||
std::string const& _additionalValueName = "",
|
||||
smt::Expression const* _additionalValue = nullptr
|
||||
);
|
||||
/// Checks that a boolean condition is not constant. Do not warn if the expression
|
||||
/// is a literal constant.
|
||||
/// @param _description the warning string, $VALUE will be replaced by the constant value.
|
||||
void checkBooleanNotConstant(
|
||||
Expression const& _condition,
|
||||
smt::Expression const& _constraints,
|
||||
smt::Expression const& _value,
|
||||
std::vector<CallStackEntry> const& _callStack,
|
||||
std::string const& _description
|
||||
std::vector<CallStackEntry> const& _callStack
|
||||
);
|
||||
std::pair<smt::CheckResult, std::vector<std::string>>
|
||||
checkSatisfiableAndGenerateModel(std::vector<smt::Expression> const& _expressionsToEvaluate);
|
||||
|
||||
@@ -965,10 +965,10 @@ pair<smt::CheckResult, vector<string>> CHC::query(smt::Expression const& _query,
|
||||
case smt::CheckResult::UNKNOWN:
|
||||
break;
|
||||
case smt::CheckResult::CONFLICTING:
|
||||
m_outerErrorReporter.warning(_location, "At least two SMT solvers provided conflicting answers. Results might not be sound.");
|
||||
m_outerErrorReporter.warning(1988_error, _location, "At least two SMT solvers provided conflicting answers. Results might not be sound.");
|
||||
break;
|
||||
case smt::CheckResult::ERROR:
|
||||
m_outerErrorReporter.warning(_location, "Error trying to invoke SMT solver.");
|
||||
m_outerErrorReporter.warning(1218_error, _location, "Error trying to invoke SMT solver.");
|
||||
break;
|
||||
}
|
||||
return {result, values};
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
|
||||
#include <boost/algorithm/string/join.hpp>
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include <boost/filesystem/operations.hpp>
|
||||
|
||||
#include <array>
|
||||
#include <fstream>
|
||||
|
||||
@@ -266,6 +266,7 @@ void SMTEncoder::endVisit(FunctionDefinition const&)
|
||||
bool SMTEncoder::visit(InlineAssembly const& _inlineAsm)
|
||||
{
|
||||
m_errorReporter.warning(
|
||||
7737_error,
|
||||
_inlineAsm.location(),
|
||||
"Assertion checker does not support inline assembly."
|
||||
);
|
||||
@@ -325,6 +326,7 @@ void SMTEncoder::endVisit(VariableDeclarationStatement const& _varDecl)
|
||||
}
|
||||
else
|
||||
m_errorReporter.warning(
|
||||
7186_error,
|
||||
_varDecl.location(),
|
||||
"Assertion checker does not yet implement such variable declarations."
|
||||
);
|
||||
@@ -350,6 +352,7 @@ void SMTEncoder::endVisit(Assignment const& _assignment)
|
||||
m_context.newValue(*varDecl);
|
||||
|
||||
m_errorReporter.warning(
|
||||
9149_error,
|
||||
_assignment.location(),
|
||||
"Assertion checker does not yet implement this assignment operator."
|
||||
);
|
||||
@@ -409,6 +412,7 @@ void SMTEncoder::endVisit(TupleExpression const& _tuple)
|
||||
|
||||
if (_tuple.isInlineArray())
|
||||
m_errorReporter.warning(
|
||||
2177_error,
|
||||
_tuple.location(),
|
||||
"Assertion checker does not yet implement inline arrays."
|
||||
);
|
||||
@@ -493,6 +497,7 @@ void SMTEncoder::endVisit(UnaryOperation const& _op)
|
||||
}
|
||||
else
|
||||
m_errorReporter.warning(
|
||||
1950_error,
|
||||
_op.location(),
|
||||
"Assertion checker does not yet implement such increments / decrements."
|
||||
);
|
||||
@@ -522,6 +527,7 @@ void SMTEncoder::endVisit(UnaryOperation const& _op)
|
||||
arrayIndexAssignment(_op.subExpression(), symbVar->currentValue());
|
||||
else
|
||||
m_errorReporter.warning(
|
||||
2683_error,
|
||||
_op.location(),
|
||||
"Assertion checker does not yet implement \"delete\" for this expression."
|
||||
);
|
||||
@@ -530,6 +536,7 @@ void SMTEncoder::endVisit(UnaryOperation const& _op)
|
||||
}
|
||||
default:
|
||||
m_errorReporter.warning(
|
||||
3682_error,
|
||||
_op.location(),
|
||||
"Assertion checker does not yet implement this operator."
|
||||
);
|
||||
@@ -568,6 +575,7 @@ void SMTEncoder::endVisit(BinaryOperation const& _op)
|
||||
compareOperation(_op);
|
||||
else
|
||||
m_errorReporter.warning(
|
||||
3876_error,
|
||||
_op.location(),
|
||||
"Assertion checker does not yet implement this operator."
|
||||
);
|
||||
@@ -580,6 +588,7 @@ void SMTEncoder::endVisit(FunctionCall const& _funCall)
|
||||
if (_funCall.annotation().kind == FunctionCallKind::StructConstructorCall)
|
||||
{
|
||||
m_errorReporter.warning(
|
||||
4639_error,
|
||||
_funCall.location(),
|
||||
"Assertion checker does not yet implement this expression."
|
||||
);
|
||||
@@ -639,6 +648,7 @@ void SMTEncoder::endVisit(FunctionCall const& _funCall)
|
||||
}
|
||||
default:
|
||||
m_errorReporter.warning(
|
||||
4588_error,
|
||||
_funCall.location(),
|
||||
"Assertion checker does not yet implement this type of function call."
|
||||
);
|
||||
@@ -771,6 +781,7 @@ void SMTEncoder::visitTypeConversion(FunctionCall const& _funCall)
|
||||
}
|
||||
|
||||
m_errorReporter.warning(
|
||||
5084_error,
|
||||
_funCall.location(),
|
||||
"Type conversion is not yet fully supported and might yield false positives."
|
||||
);
|
||||
@@ -800,6 +811,7 @@ void SMTEncoder::endVisit(Literal const& _literal)
|
||||
else
|
||||
{
|
||||
m_errorReporter.warning(
|
||||
7885_error,
|
||||
_literal.location(),
|
||||
"Assertion checker does not yet support the type of this literal (" +
|
||||
_literal.annotation().type->toString() +
|
||||
@@ -850,6 +862,7 @@ bool SMTEncoder::visit(MemberAccess const& _memberAccess)
|
||||
accessedName = identifier->name();
|
||||
else
|
||||
m_errorReporter.warning(
|
||||
9551_error,
|
||||
_memberAccess.location(),
|
||||
"Assertion checker does not yet support this expression."
|
||||
);
|
||||
@@ -879,6 +892,7 @@ bool SMTEncoder::visit(MemberAccess const& _memberAccess)
|
||||
}
|
||||
else
|
||||
m_errorReporter.warning(
|
||||
7650_error,
|
||||
_memberAccess.location(),
|
||||
"Assertion checker does not yet support this expression."
|
||||
);
|
||||
@@ -903,6 +917,7 @@ void SMTEncoder::endVisit(IndexAccess const& _indexAccess)
|
||||
if (varDecl->type()->category() == Type::Category::FixedBytes)
|
||||
{
|
||||
m_errorReporter.warning(
|
||||
7989_error,
|
||||
_indexAccess.location(),
|
||||
"Assertion checker does not yet support index accessing fixed bytes."
|
||||
);
|
||||
@@ -917,6 +932,7 @@ void SMTEncoder::endVisit(IndexAccess const& _indexAccess)
|
||||
else
|
||||
{
|
||||
m_errorReporter.warning(
|
||||
9118_error,
|
||||
_indexAccess.location(),
|
||||
"Assertion checker does not yet implement this expression."
|
||||
);
|
||||
@@ -940,6 +956,7 @@ void SMTEncoder::endVisit(IndexRangeAccess const& _indexRangeAccess)
|
||||
{
|
||||
createExpr(_indexRangeAccess);
|
||||
m_errorReporter.warning(
|
||||
2923_error,
|
||||
_indexRangeAccess.location(),
|
||||
"Assertion checker does not yet implement this expression."
|
||||
);
|
||||
@@ -1019,6 +1036,7 @@ void SMTEncoder::arrayIndexAssignment(Expression const& _expr, smt::Expression c
|
||||
else
|
||||
{
|
||||
m_errorReporter.warning(
|
||||
9056_error,
|
||||
_expr.location(),
|
||||
"Assertion checker does not yet implement this expression."
|
||||
);
|
||||
@@ -1034,6 +1052,7 @@ void SMTEncoder::defineGlobalVariable(string const& _name, Expression const& _ex
|
||||
bool abstract = m_context.createGlobalSymbol(_name, _expr);
|
||||
if (abstract)
|
||||
m_errorReporter.warning(
|
||||
1695_error,
|
||||
_expr.location(),
|
||||
"Assertion checker does not yet support this global variable."
|
||||
);
|
||||
@@ -1087,6 +1106,7 @@ void SMTEncoder::arithmeticOperation(BinaryOperation const& _op)
|
||||
}
|
||||
default:
|
||||
m_errorReporter.warning(
|
||||
5188_error,
|
||||
_op.location(),
|
||||
"Assertion checker does not yet implement this operator."
|
||||
);
|
||||
@@ -1094,6 +1114,7 @@ void SMTEncoder::arithmeticOperation(BinaryOperation const& _op)
|
||||
}
|
||||
else
|
||||
m_errorReporter.warning(
|
||||
9011_error,
|
||||
_op.location(),
|
||||
"Assertion checker does not yet implement this operator for type " + type->richIdentifier() + "."
|
||||
);
|
||||
@@ -1185,6 +1206,7 @@ void SMTEncoder::compareOperation(BinaryOperation const& _op)
|
||||
}
|
||||
else
|
||||
m_errorReporter.warning(
|
||||
7229_error,
|
||||
_op.location(),
|
||||
"Assertion checker does not yet implement the type " + _op.annotation().commonType->toString() + " for comparisons"
|
||||
);
|
||||
@@ -1213,6 +1235,7 @@ void SMTEncoder::booleanOperation(BinaryOperation const& _op)
|
||||
}
|
||||
else
|
||||
m_errorReporter.warning(
|
||||
3263_error,
|
||||
_op.location(),
|
||||
"Assertion checker does not yet implement the type " + _op.annotation().commonType->toString() + " for boolean operations"
|
||||
);
|
||||
@@ -1245,6 +1268,7 @@ void SMTEncoder::assignment(
|
||||
m_context.newValue(*varDecl);
|
||||
|
||||
m_errorReporter.warning(
|
||||
6191_error,
|
||||
_location,
|
||||
"Assertion checker does not yet implement type " + _type->toString()
|
||||
);
|
||||
@@ -1272,6 +1296,7 @@ void SMTEncoder::assignment(
|
||||
}
|
||||
else
|
||||
m_errorReporter.warning(
|
||||
8182_error,
|
||||
_location,
|
||||
"Assertion checker does not yet implement such assignments."
|
||||
);
|
||||
@@ -1482,6 +1507,7 @@ bool SMTEncoder::createVariable(VariableDeclaration const& _varDecl)
|
||||
if (abstract)
|
||||
{
|
||||
m_errorReporter.warning(
|
||||
8115_error,
|
||||
_varDecl.location(),
|
||||
"Assertion checker does not yet support the type of this variable."
|
||||
);
|
||||
@@ -1494,7 +1520,7 @@ smt::Expression SMTEncoder::expr(Expression const& _e, TypePointer _targetType)
|
||||
{
|
||||
if (!m_context.knownExpression(_e))
|
||||
{
|
||||
m_errorReporter.warning(_e.location(), "Internal error: Expression undefined for SMT solver." );
|
||||
m_errorReporter.warning(6031_error, _e.location(), "Internal error: Expression undefined for SMT solver." );
|
||||
createExpr(_e);
|
||||
}
|
||||
|
||||
@@ -1506,6 +1532,7 @@ void SMTEncoder::createExpr(Expression const& _e)
|
||||
bool abstract = m_context.createExpression(_e);
|
||||
if (abstract)
|
||||
m_errorReporter.warning(
|
||||
8364_error,
|
||||
_e.location(),
|
||||
"Assertion checker does not yet implement type " + _e.annotation().type->toString()
|
||||
);
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
|
||||
#include <boost/algorithm/string/join.hpp>
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include <boost/filesystem/operations.hpp>
|
||||
|
||||
#include <array>
|
||||
#include <fstream>
|
||||
|
||||
@@ -73,9 +73,10 @@ Json::Value ABI::generate(ContractDefinition const& _contractDef)
|
||||
);
|
||||
abi.emplace(std::move(method));
|
||||
}
|
||||
if (_contractDef.constructor())
|
||||
FunctionDefinition const* constructor = _contractDef.constructor();
|
||||
if (constructor && constructor->visibility() >= Visibility::Public)
|
||||
{
|
||||
FunctionType constrType(*_contractDef.constructor());
|
||||
FunctionType constrType(*constructor);
|
||||
FunctionType const* externalFunctionType = constrType.interfaceFunctionType();
|
||||
solAssert(!!externalFunctionType, "");
|
||||
Json::Value method;
|
||||
|
||||
@@ -67,7 +67,7 @@
|
||||
|
||||
#include <json/json.h>
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/algorithm/string/replace.hpp>
|
||||
#include <utility>
|
||||
|
||||
using namespace std;
|
||||
@@ -239,7 +239,7 @@ bool CompilerStack::parse()
|
||||
m_errorReporter.clear();
|
||||
|
||||
if (SemVerVersion{string(VersionString)}.isPrerelease())
|
||||
m_errorReporter.warning("This is a pre-release compiler version, please do not use it in production.");
|
||||
m_errorReporter.warning(3805_error, "This is a pre-release compiler version, please do not use it in production.");
|
||||
|
||||
Parser parser{m_errorReporter, m_evmVersion, m_parserErrorRecovery};
|
||||
|
||||
@@ -949,6 +949,7 @@ StringMap CompilerStack::loadMissingSources(SourceUnit const& _ast, std::string
|
||||
else
|
||||
{
|
||||
m_errorReporter.parserError(
|
||||
6275_error,
|
||||
import->location(),
|
||||
string("Source \"" + importPath + "\" not found: " + result.responseOrErrorMessage)
|
||||
);
|
||||
@@ -1110,6 +1111,7 @@ void CompilerStack::compileContract(
|
||||
compiledContract.runtimeObject.bytecode.size() > 0x6000
|
||||
)
|
||||
m_errorReporter.warning(
|
||||
5574_error,
|
||||
_contract.location(),
|
||||
"Contract code size exceeds 24576 bytes (a limit introduced in Spurious Dragon). "
|
||||
"This contract may not be deployable on mainnet. "
|
||||
|
||||
@@ -103,16 +103,14 @@ Json::Value Natspec::devDocumentation(ContractDefinition const& _contractDef)
|
||||
if (auto fun = dynamic_cast<FunctionDefinition const*>(&it.second->declaration()))
|
||||
{
|
||||
Json::Value method(devDocumentation(fun->annotation().docTags));
|
||||
// add the function, only if we have any documentation to add
|
||||
Json::Value jsonReturn = extractReturnParameterDocs(fun->annotation().docTags, *fun);
|
||||
|
||||
if (!jsonReturn.empty())
|
||||
method["returns"] = jsonReturn;
|
||||
|
||||
if (!method.empty())
|
||||
{
|
||||
// add the function, only if we have any documentation to add
|
||||
Json::Value jsonReturn = extractReturnParameterDocs(fun->annotation().docTags, *fun);
|
||||
|
||||
if (!jsonReturn.empty())
|
||||
method["returns"] = jsonReturn;
|
||||
|
||||
methods[it.second->externalSignature()] = method;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -31,8 +31,7 @@
|
||||
#include <libsolutil/JSON.h>
|
||||
#include <libsolutil/Keccak256.h>
|
||||
|
||||
#include <boost/algorithm/cxx11/any_of.hpp>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <optional>
|
||||
@@ -491,7 +490,7 @@ std::optional<Json::Value> checkOutputSelection(Json::Value const& _outputSelect
|
||||
}
|
||||
/// Validates the optimizer settings and returns them in a parsed object.
|
||||
/// On error returns the json-formatted error message.
|
||||
boost::variant<OptimiserSettings, Json::Value> parseOptimizerSettings(Json::Value const& _jsonInput)
|
||||
std::variant<OptimiserSettings, Json::Value> parseOptimizerSettings(Json::Value const& _jsonInput)
|
||||
{
|
||||
if (auto result = checkOptimizerKeys(_jsonInput))
|
||||
return *result;
|
||||
@@ -552,7 +551,7 @@ boost::variant<OptimiserSettings, Json::Value> parseOptimizerSettings(Json::Valu
|
||||
|
||||
}
|
||||
|
||||
boost::variant<StandardCompiler::InputsAndSettings, Json::Value> StandardCompiler::parseInput(Json::Value const& _input)
|
||||
std::variant<StandardCompiler::InputsAndSettings, Json::Value> StandardCompiler::parseInput(Json::Value const& _input)
|
||||
{
|
||||
InputsAndSettings ret;
|
||||
|
||||
@@ -740,10 +739,10 @@ boost::variant<StandardCompiler::InputsAndSettings, Json::Value> StandardCompile
|
||||
if (settings.isMember("optimizer"))
|
||||
{
|
||||
auto optimiserSettings = parseOptimizerSettings(settings["optimizer"]);
|
||||
if (optimiserSettings.type() == typeid(Json::Value))
|
||||
return boost::get<Json::Value>(std::move(optimiserSettings)); // was an error
|
||||
if (std::holds_alternative<Json::Value>(optimiserSettings))
|
||||
return std::get<Json::Value>(std::move(optimiserSettings)); // was an error
|
||||
else
|
||||
ret.optimiserSettings = boost::get<OptimiserSettings>(std::move(optimiserSettings));
|
||||
ret.optimiserSettings = std::get<OptimiserSettings>(std::move(optimiserSettings));
|
||||
}
|
||||
|
||||
Json::Value jsonLibraries = settings.get("libraries", Json::Value(Json::objectValue));
|
||||
@@ -1155,9 +1154,9 @@ Json::Value StandardCompiler::compile(Json::Value const& _input) noexcept
|
||||
try
|
||||
{
|
||||
auto parsed = parseInput(_input);
|
||||
if (parsed.type() == typeid(Json::Value))
|
||||
return boost::get<Json::Value>(std::move(parsed));
|
||||
InputsAndSettings settings = boost::get<InputsAndSettings>(std::move(parsed));
|
||||
if (std::holds_alternative<Json::Value>(parsed))
|
||||
return std::get<Json::Value>(std::move(parsed));
|
||||
InputsAndSettings settings = std::get<InputsAndSettings>(std::move(parsed));
|
||||
if (settings.language == "Solidity")
|
||||
return compileSolidity(std::move(settings));
|
||||
else if (settings.language == "Yul")
|
||||
|
||||
@@ -24,9 +24,9 @@
|
||||
|
||||
#include <libsolidity/interface/CompilerStack.h>
|
||||
|
||||
#include <boost/variant.hpp>
|
||||
#include <optional>
|
||||
#include <utility>
|
||||
#include <variant>
|
||||
|
||||
namespace solidity::frontend
|
||||
{
|
||||
@@ -73,7 +73,7 @@ private:
|
||||
|
||||
/// Parses the input json (and potentially invokes the read callback) and either returns
|
||||
/// it in condensed form or an error as a json object.
|
||||
boost::variant<InputsAndSettings, Json::Value> parseInput(Json::Value const& _input);
|
||||
std::variant<InputsAndSettings, Json::Value> parseInput(Json::Value const& _input);
|
||||
|
||||
Json::Value compileSolidity(InputsAndSettings _inputsAndSettings);
|
||||
Json::Value compileYul(InputsAndSettings _inputsAndSettings);
|
||||
|
||||
@@ -195,5 +195,5 @@ void DocStringParser::newTag(string const& _tagName)
|
||||
void DocStringParser::appendError(string const& _description)
|
||||
{
|
||||
m_errorsOccurred = true;
|
||||
m_errorReporter->docstringParsingError(_description);
|
||||
m_errorReporter->docstringParsingError(9440_error, _description);
|
||||
}
|
||||
|
||||
@@ -103,7 +103,7 @@ ASTPointer<SourceUnit> Parser::parse(shared_ptr<Scanner> const& _scanner)
|
||||
nodes.push_back(parseEnumDefinition());
|
||||
break;
|
||||
default:
|
||||
fatalParserError(string("Expected pragma, import directive or contract/interface/library/struct/enum definition."));
|
||||
fatalParserError(7858_error, "Expected pragma, import directive or contract/interface/library/struct/enum definition.");
|
||||
}
|
||||
}
|
||||
solAssert(m_recursionDepth == 0, "");
|
||||
@@ -128,9 +128,10 @@ void Parser::parsePragmaVersion(SourceLocation const& _location, vector<Token> c
|
||||
// so we don't need to report anything here.
|
||||
if (!m_parserErrorRecovery)
|
||||
m_errorReporter.fatalParserError(
|
||||
5333_error,
|
||||
_location,
|
||||
"Source file requires different compiler version (current compiler is " +
|
||||
string(VersionString) + " - note that nightly builds are considered to be "
|
||||
string(VersionString) + ") - note that nightly builds are considered to be "
|
||||
"strictly less than the released version"
|
||||
);
|
||||
}
|
||||
@@ -162,7 +163,7 @@ ASTPointer<PragmaDirective> Parser::parsePragmaDirective()
|
||||
{
|
||||
Token token = m_scanner->currentToken();
|
||||
if (token == Token::Illegal)
|
||||
parserError("Token incompatible with Solidity parser as part of pragma directive.");
|
||||
parserError(6281_error, "Token incompatible with Solidity parser as part of pragma directive.");
|
||||
else
|
||||
{
|
||||
string literal = m_scanner->currentLiteral();
|
||||
@@ -240,18 +241,18 @@ ASTPointer<ImportDirective> Parser::parseImportDirective()
|
||||
unitAlias = expectIdentifierToken();
|
||||
}
|
||||
else
|
||||
fatalParserError("Expected string literal (path), \"*\" or alias list.");
|
||||
fatalParserError(9478_error, "Expected string literal (path), \"*\" or alias list.");
|
||||
// "from" is not a keyword but parsed as an identifier because of backwards
|
||||
// compatibility and because it is a really common word.
|
||||
if (m_scanner->currentToken() != Token::Identifier || m_scanner->currentLiteral() != "from")
|
||||
fatalParserError("Expected \"from\".");
|
||||
fatalParserError(8208_error, "Expected \"from\".");
|
||||
m_scanner->next();
|
||||
if (m_scanner->currentToken() != Token::StringLiteral)
|
||||
fatalParserError("Expected import path.");
|
||||
fatalParserError(6845_error, "Expected import path.");
|
||||
path = getLiteralAndAdvance();
|
||||
}
|
||||
if (path->empty())
|
||||
fatalParserError("Import path cannot be empty.");
|
||||
fatalParserError(6326_error, "Import path cannot be empty.");
|
||||
nodeFactory.markEndPosition();
|
||||
expectToken(Token::Semicolon);
|
||||
return nodeFactory.createNode<ImportDirective>(path, unitAlias, move(symbolAliases));
|
||||
@@ -278,7 +279,7 @@ std::pair<ContractKind, bool> Parser::parseContractKind()
|
||||
kind = ContractKind::Library;
|
||||
break;
|
||||
default:
|
||||
parserError("Expected keyword \"contract\", \"interface\" or \"library\".");
|
||||
parserError(3515_error, "Expected keyword \"contract\", \"interface\" or \"library\".");
|
||||
return std::make_pair(ContractKind::Contract, abstract);
|
||||
}
|
||||
m_scanner->next();
|
||||
@@ -343,7 +344,7 @@ ASTPointer<ContractDefinition> Parser::parseContractDefinition()
|
||||
else if (currentTokenValue == Token::Using)
|
||||
subNodes.push_back(parseUsingDirective());
|
||||
else
|
||||
fatalParserError(string("Function, variable, struct or modifier declaration expected."));
|
||||
fatalParserError(9182_error, "Function, variable, struct or modifier declaration expected.");
|
||||
}
|
||||
}
|
||||
catch (FatalError const&)
|
||||
@@ -462,6 +463,7 @@ StateMutability Parser::parseStateMutability()
|
||||
case Token::Constant:
|
||||
stateMutability = StateMutability::View;
|
||||
parserError(
|
||||
7698_error,
|
||||
"The state mutability modifier \"constant\" was removed in version 0.5.0. "
|
||||
"Use \"view\" or \"pure\" instead."
|
||||
);
|
||||
@@ -494,11 +496,12 @@ Parser::FunctionHeaderParserResult Parser::parseFunctionHeader(bool _isStateVari
|
||||
// Detect this and return early.
|
||||
if (_isStateVariable && (result.visibility == Visibility::External || result.visibility == Visibility::Internal))
|
||||
break;
|
||||
parserError(string(
|
||||
parserError(
|
||||
9439_error,
|
||||
"Visibility already specified as \"" +
|
||||
Declaration::visibilityToString(result.visibility) +
|
||||
"\"."
|
||||
));
|
||||
);
|
||||
m_scanner->next();
|
||||
}
|
||||
else
|
||||
@@ -508,11 +511,12 @@ Parser::FunctionHeaderParserResult Parser::parseFunctionHeader(bool _isStateVari
|
||||
{
|
||||
if (result.stateMutability != StateMutability::NonPayable)
|
||||
{
|
||||
parserError(string(
|
||||
parserError(
|
||||
9680_error,
|
||||
"State mutability already specified as \"" +
|
||||
stateMutabilityToString(result.stateMutability) +
|
||||
"\"."
|
||||
));
|
||||
);
|
||||
m_scanner->next();
|
||||
}
|
||||
else
|
||||
@@ -521,14 +525,14 @@ Parser::FunctionHeaderParserResult Parser::parseFunctionHeader(bool _isStateVari
|
||||
else if (!_isStateVariable && token == Token::Override)
|
||||
{
|
||||
if (result.overrides)
|
||||
parserError("Override already specified.");
|
||||
parserError(1827_error, "Override already specified.");
|
||||
|
||||
result.overrides = parseOverrideSpecifier();
|
||||
}
|
||||
else if (!_isStateVariable && token == Token::Virtual)
|
||||
{
|
||||
if (result.isVirtual)
|
||||
parserError("Virtual already specified.");
|
||||
parserError(6879_error, "Virtual already specified.");
|
||||
|
||||
result.isVirtual = true;
|
||||
m_scanner->next();
|
||||
@@ -576,9 +580,9 @@ ASTPointer<ASTNode> Parser::parseFunctionDefinition()
|
||||
"the \"function\" keyword to define it."
|
||||
};
|
||||
if (m_scanner->currentToken() == Token::Constructor)
|
||||
parserError(message);
|
||||
parserError(3323_error, message);
|
||||
else
|
||||
parserWarning(message);
|
||||
parserWarning(3445_error, message);
|
||||
m_scanner->next();
|
||||
}
|
||||
else
|
||||
@@ -659,10 +663,10 @@ ASTPointer<EnumDefinition> Parser::parseEnumDefinition()
|
||||
break;
|
||||
expectToken(Token::Comma);
|
||||
if (m_scanner->currentToken() != Token::Identifier)
|
||||
fatalParserError(string("Expected identifier after ','"));
|
||||
fatalParserError(1612_error, "Expected identifier after ','");
|
||||
}
|
||||
if (members.empty())
|
||||
parserError({"enum with no members is not allowed."});
|
||||
parserError(3147_error, "enum with no members is not allowed.");
|
||||
|
||||
nodeFactory.markEndPosition();
|
||||
expectToken(Token::RBrace);
|
||||
@@ -689,6 +693,7 @@ ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(
|
||||
|
||||
if (dynamic_cast<FunctionTypeName*>(type.get()) && _options.isStateVariable && m_scanner->currentToken() == Token::LBrace)
|
||||
fatalParserError(
|
||||
2915_error,
|
||||
"Expected a state variable declaration. If you intended this as a fallback function "
|
||||
"or a function to handle plain ether transactions, use the \"fallback\" keyword "
|
||||
"or the \"receive\" keyword instead."
|
||||
@@ -709,11 +714,12 @@ ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(
|
||||
nodeFactory.markEndPosition();
|
||||
if (visibility != Visibility::Default)
|
||||
{
|
||||
parserError(string(
|
||||
parserError(
|
||||
4110_error,
|
||||
"Visibility already specified as \"" +
|
||||
Declaration::visibilityToString(visibility) +
|
||||
"\"."
|
||||
));
|
||||
);
|
||||
m_scanner->next();
|
||||
}
|
||||
else
|
||||
@@ -722,7 +728,7 @@ ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(
|
||||
else if (_options.isStateVariable && token == Token::Override)
|
||||
{
|
||||
if (overrides)
|
||||
parserError("Override already specified.");
|
||||
parserError(9125_error, "Override already specified.");
|
||||
|
||||
overrides = parseOverrideSpecifier();
|
||||
}
|
||||
@@ -734,6 +740,7 @@ ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(
|
||||
{
|
||||
if (mutability != VariableDeclaration::Mutability::Mutable)
|
||||
parserError(
|
||||
3109_error,
|
||||
string("Mutability already set to ") +
|
||||
(mutability == VariableDeclaration::Mutability::Constant ? "\"constant\"" : "\"immutable\"")
|
||||
);
|
||||
@@ -745,9 +752,9 @@ ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(
|
||||
else if (_options.allowLocationSpecifier && TokenTraits::isLocationSpecifier(token))
|
||||
{
|
||||
if (location != VariableDeclaration::Location::Unspecified)
|
||||
parserError(string("Location already specified."));
|
||||
parserError(3548_error, "Location already specified.");
|
||||
else if (!type)
|
||||
parserError(string("Location specifier needs explicit type name."));
|
||||
parserError(7439_error, "Location specifier needs explicit type name.");
|
||||
else
|
||||
{
|
||||
switch (token)
|
||||
@@ -836,13 +843,13 @@ ASTPointer<ModifierDefinition> Parser::parseModifierDefinition()
|
||||
if (m_scanner->currentToken() == Token::Override)
|
||||
{
|
||||
if (overrides)
|
||||
parserError("Override already specified.");
|
||||
parserError(9102_error, "Override already specified.");
|
||||
overrides = parseOverrideSpecifier();
|
||||
}
|
||||
else if (m_scanner->currentToken() == Token::Virtual)
|
||||
{
|
||||
if (isVirtual)
|
||||
parserError("Virtual already specified.");
|
||||
parserError(2662_error, "Virtual already specified.");
|
||||
|
||||
isVirtual = true;
|
||||
m_scanner->next();
|
||||
@@ -990,7 +997,7 @@ ASTPointer<TypeName> Parser::parseTypeName(bool _allowVar)
|
||||
}
|
||||
else
|
||||
{
|
||||
parserError("State mutability can only be specified for address types.");
|
||||
parserError(9106_error, "State mutability can only be specified for address types.");
|
||||
m_scanner->next();
|
||||
}
|
||||
}
|
||||
@@ -999,7 +1006,7 @@ ASTPointer<TypeName> Parser::parseTypeName(bool _allowVar)
|
||||
else if (token == Token::Var)
|
||||
{
|
||||
if (!_allowVar)
|
||||
parserError(string("Expected explicit type name."));
|
||||
parserError(7059_error, "Expected explicit type name.");
|
||||
m_scanner->next();
|
||||
}
|
||||
else if (token == Token::Function)
|
||||
@@ -1009,7 +1016,7 @@ ASTPointer<TypeName> Parser::parseTypeName(bool _allowVar)
|
||||
else if (token == Token::Identifier)
|
||||
type = parseUserDefinedTypeName();
|
||||
else
|
||||
fatalParserError(string("Expected type name"));
|
||||
fatalParserError(3546_error, "Expected type name");
|
||||
|
||||
if (type)
|
||||
// Parse "[...]" postfixes for arrays.
|
||||
@@ -1052,7 +1059,7 @@ ASTPointer<Mapping> Parser::parseMapping()
|
||||
m_scanner->next();
|
||||
}
|
||||
else
|
||||
fatalParserError(string("Expected elementary type name or identifier for mapping key type"));
|
||||
fatalParserError(1005_error, "Expected elementary type name or identifier for mapping key type");
|
||||
expectToken(Token::Arrow);
|
||||
bool const allowVar = false;
|
||||
ASTPointer<TypeName> valueType = parseTypeName(allowVar);
|
||||
@@ -1078,7 +1085,7 @@ ASTPointer<ParameterList> Parser::parseParameterList(
|
||||
while (m_scanner->currentToken() != Token::RParen)
|
||||
{
|
||||
if (m_scanner->currentToken() == Token::Comma && m_scanner->peekNextToken() == Token::RParen)
|
||||
fatalParserError("Unexpected trailing comma in parameter list.");
|
||||
fatalParserError(7591_error, "Unexpected trailing comma in parameter list.");
|
||||
expectToken(Token::Comma);
|
||||
parameters.push_back(parseVariableDeclaration(options));
|
||||
}
|
||||
@@ -1213,7 +1220,7 @@ ASTPointer<InlineAssembly> Parser::parseInlineAssembly(ASTPointer<ASTString> con
|
||||
if (m_scanner->currentToken() == Token::StringLiteral)
|
||||
{
|
||||
if (m_scanner->currentLiteral() != "evmasm")
|
||||
fatalParserError("Only \"evmasm\" supported.");
|
||||
fatalParserError(4531_error, "Only \"evmasm\" supported.");
|
||||
// This can be used in the future to set the dialect.
|
||||
m_scanner->next();
|
||||
}
|
||||
@@ -1376,7 +1383,7 @@ ASTPointer<EmitStatement> Parser::parseEmitStatement(ASTPointer<ASTString> const
|
||||
ASTNodeFactory eventCallNodeFactory(*this);
|
||||
|
||||
if (m_scanner->currentToken() != Token::Identifier)
|
||||
fatalParserError("Expected event name or path.");
|
||||
fatalParserError(5620_error, "Expected event name or path.");
|
||||
|
||||
IndexAccessedPath iap;
|
||||
while (true)
|
||||
@@ -1844,7 +1851,7 @@ ASTPointer<Expression> Parser::parsePrimaryExpression()
|
||||
nodeFactory.markEndPosition();
|
||||
m_scanner->next();
|
||||
if (m_scanner->currentToken() == Token::Illegal)
|
||||
fatalParserError(to_string(m_scanner->currentError()));
|
||||
fatalParserError(5428_error, to_string(m_scanner->currentError()));
|
||||
expression = nodeFactory.createNode<Literal>(token, make_shared<ASTString>(literal));
|
||||
break;
|
||||
}
|
||||
@@ -1875,7 +1882,7 @@ ASTPointer<Expression> Parser::parsePrimaryExpression()
|
||||
if (m_scanner->currentToken() != Token::Comma && m_scanner->currentToken() != oppositeToken)
|
||||
components.push_back(parseExpression());
|
||||
else if (isArray)
|
||||
parserError("Expected expression (inline array elements cannot be omitted).");
|
||||
parserError(4799_error, "Expected expression (inline array elements cannot be omitted).");
|
||||
else
|
||||
components.push_back(ASTPointer<Expression>());
|
||||
|
||||
@@ -1890,7 +1897,7 @@ ASTPointer<Expression> Parser::parsePrimaryExpression()
|
||||
break;
|
||||
}
|
||||
case Token::Illegal:
|
||||
fatalParserError(to_string(m_scanner->currentError()));
|
||||
fatalParserError(8936_error, to_string(m_scanner->currentError()));
|
||||
break;
|
||||
default:
|
||||
if (TokenTraits::isElementaryTypeName(token))
|
||||
@@ -1906,7 +1913,7 @@ ASTPointer<Expression> Parser::parsePrimaryExpression()
|
||||
m_scanner->next();
|
||||
}
|
||||
else
|
||||
fatalParserError(string("Expected primary expression."));
|
||||
fatalParserError(6933_error, "Expected primary expression.");
|
||||
break;
|
||||
}
|
||||
return expression;
|
||||
@@ -1964,7 +1971,7 @@ pair<vector<ASTPointer<Expression>>, vector<ASTPointer<ASTString>>> Parser::pars
|
||||
m_scanner->peekNextToken() == Token::RBrace
|
||||
)
|
||||
{
|
||||
parserError("Unexpected trailing comma.");
|
||||
parserError(2074_error, "Unexpected trailing comma.");
|
||||
m_scanner->next();
|
||||
}
|
||||
|
||||
@@ -2083,7 +2090,7 @@ ASTPointer<TypeName> Parser::typeNameFromIndexAccessStructure(Parser::IndexAcces
|
||||
for (auto const& lengthExpression: _iap.indices)
|
||||
{
|
||||
if (lengthExpression.end)
|
||||
parserError(lengthExpression.location, "Expected array length expression.");
|
||||
parserError(5464_error, lengthExpression.location, "Expected array length expression.");
|
||||
nodeFactory.setLocation(lengthExpression.location);
|
||||
type = nodeFactory.createNode<ArrayTypeName>(type, lengthExpression.start);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user