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:
commit
69a028b49c
@ -531,7 +531,7 @@ jobs:
|
||||
xcode: "11.0.0"
|
||||
environment:
|
||||
TERM: xterm
|
||||
CMAKE_BUILD_TYPE: Debug
|
||||
CMAKE_BUILD_TYPE: Release
|
||||
steps:
|
||||
- checkout
|
||||
- restore_cache:
|
||||
|
@ -30,7 +30,9 @@ Compiler Features:
|
||||
Bugfixes:
|
||||
* Optimizer: Fixed a bug in BlockDeDuplicator.
|
||||
* Type Checker: Disallow assignments to storage variables of type ``mapping``.
|
||||
|
||||
* NatSpec: DocString block is terminated when encountering an empty line.
|
||||
* Scanner: Fix bug when two empty NatSpec comments lead to scanning past EOL.
|
||||
* Code Generator: Trigger proper unimplemented errors on certain array copy operations.
|
||||
|
||||
### 0.6.8 (2020-05-14)
|
||||
|
||||
|
@ -166,7 +166,7 @@ This section gives detailed instructions on how to update prior code for every b
|
||||
documentation so long as the notices are in the order they appear in the tuple return type.
|
||||
|
||||
* Choose unique identifiers for variable declarations in inline assembly that do not conflict
|
||||
with declartions outside the inline assembly block.
|
||||
with declarations outside the inline assembly block.
|
||||
|
||||
* Add ``virtual`` to every non-interface function you intend to override. Add ``virtual``
|
||||
to all functions without implementation outside interfaces. For single inheritance, add
|
||||
|
@ -435,10 +435,10 @@ map<u256, u256> Assembly::optimiseInternal(
|
||||
// This only modifies PushTags, we have to run again to actually remove code.
|
||||
if (_settings.runDeduplicate)
|
||||
{
|
||||
BlockDeduplicator dedup{m_items};
|
||||
if (dedup.deduplicate())
|
||||
BlockDeduplicator deduplicator{m_items};
|
||||
if (deduplicator.deduplicate())
|
||||
{
|
||||
for (auto const& replacement: dedup.replacedTags())
|
||||
for (auto const& replacement: deduplicator.replacedTags())
|
||||
{
|
||||
assertThrow(
|
||||
replacement.first <= size_t(-1) && replacement.second <= size_t(-1),
|
||||
|
@ -267,10 +267,13 @@ bool Scanner::skipWhitespace()
|
||||
return sourcePos() != startPosition;
|
||||
}
|
||||
|
||||
void Scanner::skipWhitespaceExceptUnicodeLinebreak()
|
||||
bool Scanner::skipWhitespaceExceptUnicodeLinebreak()
|
||||
{
|
||||
int const startPosition = sourcePos();
|
||||
while (isWhiteSpace(m_char) && !isUnicodeLinebreak())
|
||||
advance();
|
||||
// Return whether or not we skipped any characters.
|
||||
return sourcePos() != startPosition;
|
||||
}
|
||||
|
||||
Token Scanner::skipSingleLineComment()
|
||||
@ -321,7 +324,7 @@ int Scanner::scanSingleLineDocComment()
|
||||
{
|
||||
// Check if next line is also a single-line comment.
|
||||
// If any whitespaces were skipped, use source position before.
|
||||
if (!skipWhitespace())
|
||||
if (!skipWhitespaceExceptUnicodeLinebreak())
|
||||
endPosition = m_source->position();
|
||||
|
||||
if (!m_source->isPastEndOfInput(3) &&
|
||||
@ -329,8 +332,10 @@ int Scanner::scanSingleLineDocComment()
|
||||
m_source->get(1) == '/' &&
|
||||
m_source->get(2) == '/')
|
||||
{
|
||||
addCommentLiteralChar('\n');
|
||||
m_char = m_source->advanceAndGet(3);
|
||||
if (atEndOfLine())
|
||||
continue;
|
||||
addCommentLiteralChar('\n');
|
||||
}
|
||||
else
|
||||
break; // next line is not a documentation comment, we are done
|
||||
@ -389,9 +394,11 @@ Token Scanner::scanMultiLineDocComment()
|
||||
}
|
||||
else if (!m_source->isPastEndOfInput(1) && m_source->get(0) == '*' && m_source->get(1) != '/')
|
||||
{ // skip first '*' in subsequent lines
|
||||
m_char = m_source->advanceAndGet(1);
|
||||
if (atEndOfLine()) // ignores empty lines
|
||||
continue;
|
||||
if (charsAdded)
|
||||
addCommentLiteralChar('\n');
|
||||
m_char = m_source->advanceAndGet(2);
|
||||
addCommentLiteralChar('\n'); // corresponds to the end of previous line
|
||||
}
|
||||
else if (!m_source->isPastEndOfInput(1) && m_source->get(0) == '*' && m_source->get(1) == '/')
|
||||
{ // if after newline the comment ends, don't insert the newline
|
||||
|
@ -214,7 +214,7 @@ private:
|
||||
/// Skips all whitespace and @returns true if something was skipped.
|
||||
bool skipWhitespace();
|
||||
/// Skips all whitespace that are neither '\r' nor '\n'.
|
||||
void skipWhitespaceExceptUnicodeLinebreak();
|
||||
bool skipWhitespaceExceptUnicodeLinebreak();
|
||||
Token skipSingleLineComment();
|
||||
Token skipMultiLineComment();
|
||||
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include <liblangutil/SourceReferenceFormatterHuman.h>
|
||||
#include <liblangutil/Scanner.h>
|
||||
#include <liblangutil/Exceptions.h>
|
||||
#include <libsolutil/UTF8.h>
|
||||
#include <iomanip>
|
||||
|
||||
using namespace std;
|
||||
@ -103,12 +104,13 @@ void SourceReferenceFormatterHuman::printSourceLocation(SourceReference const& _
|
||||
m_stream << leftpad << ' ';
|
||||
frameColored() << '|';
|
||||
m_stream << ' ';
|
||||
|
||||
for_each(
|
||||
_ref.text.cbegin(),
|
||||
_ref.text.cbegin() + _ref.startColumn,
|
||||
_ref.text.cbegin() + numCodepoints(_ref.text.substr(0, _ref.startColumn)),
|
||||
[this](char ch) { m_stream << (ch == '\t' ? '\t' : ' '); }
|
||||
);
|
||||
diagColored() << string(locationLength, '^');
|
||||
diagColored() << string(numCodepoints(_ref.text.substr(_ref.startColumn, locationLength)), '^');
|
||||
m_stream << '\n';
|
||||
}
|
||||
else
|
||||
|
@ -50,7 +50,8 @@ bool DeclarationTypeChecker::visit(ElementaryTypeName const& _typeName)
|
||||
_typeName.annotation().type = TypeProvider::address();
|
||||
break;
|
||||
default:
|
||||
typeError(
|
||||
m_errorReporter.typeError(
|
||||
2311_error,
|
||||
_typeName.location(),
|
||||
"Address types can only be payable or non-payable."
|
||||
);
|
||||
@ -102,7 +103,11 @@ bool DeclarationTypeChecker::visit(StructDefinition const& _struct)
|
||||
auto visitor = [&](StructDefinition const& _struct, auto& _cycleDetector, size_t _depth)
|
||||
{
|
||||
if (_depth >= 256)
|
||||
fatalDeclarationError(_struct.location(), "Struct definition exhausts cyclic dependency validator.");
|
||||
m_errorReporter.fatalDeclarationError(
|
||||
5651_error,
|
||||
_struct.location(),
|
||||
"Struct definition exhausts cyclic dependency validator."
|
||||
);
|
||||
|
||||
for (ASTPointer<VariableDeclaration> const& member: _struct.members())
|
||||
{
|
||||
@ -119,7 +124,7 @@ bool DeclarationTypeChecker::visit(StructDefinition const& _struct)
|
||||
}
|
||||
};
|
||||
if (util::CycleDetector<StructDefinition>(visitor).run(_struct) != nullptr)
|
||||
fatalTypeError(_struct.location(), "Recursive struct definition.");
|
||||
m_errorReporter.fatalTypeError(2046_error, _struct.location(), "Recursive struct definition.");
|
||||
|
||||
return false;
|
||||
}
|
||||
@ -145,9 +150,14 @@ void DeclarationTypeChecker::endVisit(UserDefinedTypeName const& _typeName)
|
||||
else
|
||||
{
|
||||
_typeName.annotation().type = TypeProvider::emptyTuple();
|
||||
fatalTypeError(_typeName.location(), "Name has to refer to a struct, enum or contract.");
|
||||
m_errorReporter.fatalTypeError(
|
||||
9755_error,
|
||||
_typeName.location(),
|
||||
"Name has to refer to a struct, enum or contract."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
bool DeclarationTypeChecker::visit(FunctionTypeName const& _typeName)
|
||||
{
|
||||
if (_typeName.annotation().type)
|
||||
@ -165,18 +175,27 @@ bool DeclarationTypeChecker::visit(FunctionTypeName const& _typeName)
|
||||
case Visibility::External:
|
||||
break;
|
||||
default:
|
||||
fatalTypeError(_typeName.location(), "Invalid visibility, can only be \"external\" or \"internal\".");
|
||||
m_errorReporter.fatalTypeError(
|
||||
7653_error,
|
||||
_typeName.location(),
|
||||
"Invalid visibility, can only be \"external\" or \"internal\"."
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_typeName.isPayable() && _typeName.visibility() != Visibility::External)
|
||||
{
|
||||
fatalTypeError(_typeName.location(), "Only external function types can be payable.");
|
||||
m_errorReporter.fatalTypeError(
|
||||
6138_error,
|
||||
_typeName.location(),
|
||||
"Only external function types can be payable."
|
||||
);
|
||||
return false;
|
||||
}
|
||||
_typeName.annotation().type = TypeProvider::function(_typeName);
|
||||
return false;
|
||||
}
|
||||
|
||||
void DeclarationTypeChecker::endVisit(Mapping const& _mapping)
|
||||
{
|
||||
if (_mapping.annotation().type)
|
||||
@ -226,7 +245,11 @@ void DeclarationTypeChecker::endVisit(ArrayTypeName const& _typeName)
|
||||
return;
|
||||
}
|
||||
if (baseType->storageBytes() == 0)
|
||||
fatalTypeError(_typeName.baseType().location(), "Illegal base type of storage size zero for array.");
|
||||
m_errorReporter.fatalTypeError(
|
||||
9390_error,
|
||||
_typeName.baseType().location(),
|
||||
"Illegal base type of storage size zero for array."
|
||||
);
|
||||
if (Expression const* length = _typeName.length())
|
||||
{
|
||||
TypePointer& lengthTypeGeneric = length->annotation().type;
|
||||
@ -235,13 +258,17 @@ void DeclarationTypeChecker::endVisit(ArrayTypeName const& _typeName)
|
||||
RationalNumberType const* lengthType = dynamic_cast<RationalNumberType const*>(lengthTypeGeneric);
|
||||
u256 lengthValue = 0;
|
||||
if (!lengthType || !lengthType->mobileType())
|
||||
typeError(length->location(), "Invalid array length, expected integer literal or constant expression.");
|
||||
m_errorReporter.typeError(
|
||||
8922_error,
|
||||
length->location(),
|
||||
"Invalid array length, expected integer literal or constant expression."
|
||||
);
|
||||
else if (lengthType->isZero())
|
||||
typeError(length->location(), "Array with zero length specified.");
|
||||
m_errorReporter.typeError(1220_error, length->location(), "Array with zero length specified.");
|
||||
else if (lengthType->isFractional())
|
||||
typeError(length->location(), "Array with fractional length specified.");
|
||||
m_errorReporter.typeError(4323_error, length->location(), "Array with fractional length specified.");
|
||||
else if (lengthType->isNegative())
|
||||
typeError(length->location(), "Array with negative length specified.");
|
||||
m_errorReporter.typeError(9308_error, length->location(), "Array with negative length specified.");
|
||||
else
|
||||
lengthValue = lengthType->literalValue(nullptr);
|
||||
_typeName.annotation().type = TypeProvider::array(DataLocation::Storage, baseType, lengthValue);
|
||||
@ -249,15 +276,24 @@ void DeclarationTypeChecker::endVisit(ArrayTypeName const& _typeName)
|
||||
else
|
||||
_typeName.annotation().type = TypeProvider::array(DataLocation::Storage, baseType);
|
||||
}
|
||||
|
||||
void DeclarationTypeChecker::endVisit(VariableDeclaration const& _variable)
|
||||
{
|
||||
if (_variable.annotation().type)
|
||||
return;
|
||||
|
||||
if (_variable.isConstant() && !_variable.isStateVariable())
|
||||
m_errorReporter.declarationError(1788_error, _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(8297_error, _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())
|
||||
{
|
||||
@ -309,7 +345,7 @@ void DeclarationTypeChecker::endVisit(VariableDeclaration const& _variable)
|
||||
errorString += " for variable";
|
||||
}
|
||||
errorString += ", but " + locationToString(varLoc) + " was given.";
|
||||
typeError(_variable.location(), errorString);
|
||||
m_errorReporter.typeError(6160_error, _variable.location(), errorString);
|
||||
|
||||
solAssert(!allowedDataLocations.empty(), "");
|
||||
varLoc = *allowedDataLocations.begin();
|
||||
@ -359,24 +395,9 @@ void DeclarationTypeChecker::endVisit(VariableDeclaration const& _variable)
|
||||
|
||||
}
|
||||
|
||||
void DeclarationTypeChecker::typeError(SourceLocation const& _location, string const& _description)
|
||||
{
|
||||
m_errorReporter.typeError(2311_error, _location, _description);
|
||||
}
|
||||
|
||||
void DeclarationTypeChecker::fatalTypeError(SourceLocation const& _location, string const& _description)
|
||||
{
|
||||
m_errorReporter.fatalTypeError(5651_error, _location, _description);
|
||||
}
|
||||
|
||||
void DeclarationTypeChecker::fatalDeclarationError(SourceLocation const& _location, string const& _description)
|
||||
{
|
||||
m_errorReporter.fatalDeclarationError(2046_error, _location, _description);
|
||||
}
|
||||
|
||||
bool DeclarationTypeChecker::check(ASTNode const& _node)
|
||||
{
|
||||
unsigned errorCount = m_errorReporter.errorCount();
|
||||
auto watcher = m_errorReporter.errorWatcher();
|
||||
_node.accept(*this);
|
||||
return m_errorReporter.errorCount() == errorCount;
|
||||
return watcher.ok();
|
||||
}
|
||||
|
@ -59,15 +59,6 @@ private:
|
||||
void endVisit(VariableDeclaration const& _variable) override;
|
||||
bool visit(StructDefinition const& _struct) override;
|
||||
|
||||
/// Adds a new error to the list of errors.
|
||||
void typeError(langutil::SourceLocation const& _location, std::string const& _description);
|
||||
|
||||
/// Adds a new error to the list of errors and throws to abort reference resolving.
|
||||
void fatalTypeError(langutil::SourceLocation const& _location, std::string const& _description);
|
||||
|
||||
/// Adds a new error to the list of errors and throws to abort reference resolving.
|
||||
void fatalDeclarationError(langutil::SourceLocation const& _location, std::string const& _description);
|
||||
|
||||
langutil::ErrorReporter& m_errorReporter;
|
||||
langutil::EVMVersion m_evmVersion;
|
||||
bool m_insideFunctionType = false;
|
||||
|
@ -68,14 +68,16 @@ bool DocStringAnalyser::visit(VariableDeclaration const& _variable)
|
||||
parseDocStrings(_variable, _variable.annotation(), validPublicTags, "non-public state variables");
|
||||
if (_variable.annotation().docTags.count("notice") > 0)
|
||||
m_errorReporter.warning(
|
||||
9098_error, _variable.documentation()->location(),
|
||||
"Documentation tag on non-public state variables will be disallowed in 0.7.0. You will need to use the @dev tag explicitly."
|
||||
7816_error, _variable.documentation()->location(),
|
||||
"Documentation tag on non-public state variables will be disallowed in 0.7.0. "
|
||||
"You will need to use the @dev tag explicitly."
|
||||
);
|
||||
}
|
||||
if (_variable.annotation().docTags.count("title") > 0 || _variable.annotation().docTags.count("author") > 0)
|
||||
m_errorReporter.warning(
|
||||
4822_error, _variable.documentation()->location(),
|
||||
"Documentation tag @title and @author is only allowed on contract definitions. It will be disallowed in 0.7.0."
|
||||
8532_error, _variable.documentation()->location(),
|
||||
"Documentation tag @title and @author is only allowed on contract definitions. "
|
||||
"It will be disallowed in 0.7.0."
|
||||
);
|
||||
}
|
||||
return false;
|
||||
@ -110,7 +112,8 @@ void DocStringAnalyser::checkParameters(
|
||||
auto paramRange = _annotation.docTags.equal_range("param");
|
||||
for (auto i = paramRange.first; i != paramRange.second; ++i)
|
||||
if (!validParams.count(i->second.paramName))
|
||||
appendError(
|
||||
m_errorReporter.docstringParsingError(
|
||||
3881_error,
|
||||
_node.documentation()->location(),
|
||||
"Documented parameter \"" +
|
||||
i->second.paramName +
|
||||
@ -159,7 +162,8 @@ void DocStringAnalyser::parseDocStrings(
|
||||
for (auto const& docTag: _annotation.docTags)
|
||||
{
|
||||
if (!_validTags.count(docTag.first))
|
||||
appendError(
|
||||
m_errorReporter.docstringParsingError(
|
||||
6546_error,
|
||||
_node.documentation()->location(),
|
||||
"Documentation tag @" + docTag.first + " not valid for " + _nodeName + "."
|
||||
);
|
||||
@ -170,12 +174,14 @@ void DocStringAnalyser::parseDocStrings(
|
||||
if (auto* varDecl = dynamic_cast<VariableDeclaration const*>(&_node))
|
||||
{
|
||||
if (!varDecl->isPublic())
|
||||
appendError(
|
||||
m_errorReporter.docstringParsingError(
|
||||
9440_error,
|
||||
_node.documentation()->location(),
|
||||
"Documentation tag \"@" + docTag.first + "\" is only allowed on public state-variables."
|
||||
);
|
||||
if (returnTagsVisited > 1)
|
||||
appendError(
|
||||
m_errorReporter.docstringParsingError(
|
||||
5256_error,
|
||||
_node.documentation()->location(),
|
||||
"Documentation tag \"@" + docTag.first + "\" is only allowed once on state-variables."
|
||||
);
|
||||
@ -186,7 +192,8 @@ void DocStringAnalyser::parseDocStrings(
|
||||
string firstWord = content.substr(0, content.find_first_of(" \t"));
|
||||
|
||||
if (returnTagsVisited > function->returnParameters().size())
|
||||
appendError(
|
||||
m_errorReporter.docstringParsingError(
|
||||
2604_error,
|
||||
_node.documentation()->location(),
|
||||
"Documentation tag \"@" + docTag.first + " " + docTag.second.content + "\"" +
|
||||
" exceeds the number of return parameters."
|
||||
@ -195,7 +202,8 @@ void DocStringAnalyser::parseDocStrings(
|
||||
{
|
||||
auto parameter = function->returnParameters().at(returnTagsVisited - 1);
|
||||
if (!parameter->name().empty() && parameter->name() != firstWord)
|
||||
appendError(
|
||||
m_errorReporter.docstringParsingError(
|
||||
5856_error,
|
||||
_node.documentation()->location(),
|
||||
"Documentation tag \"@" + docTag.first + " " + docTag.second.content + "\"" +
|
||||
" does not contain the name of its return parameter."
|
||||
@ -205,8 +213,3 @@ void DocStringAnalyser::parseDocStrings(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DocStringAnalyser::appendError(SourceLocation const& _location, string const& _description)
|
||||
{
|
||||
m_errorReporter.docstringParsingError(7816_error, _location, _description);
|
||||
}
|
||||
|
@ -81,8 +81,6 @@ private:
|
||||
std::string const& _nodeName
|
||||
);
|
||||
|
||||
void appendError(langutil::SourceLocation const& _location, std::string const& _description);
|
||||
|
||||
langutil::ErrorReporter& m_errorReporter;
|
||||
};
|
||||
|
||||
|
@ -119,7 +119,7 @@ bool ReferencesResolver::visit(Identifier const& _identifier)
|
||||
else
|
||||
errorMessage += " Did you mean " + std::move(suggestions) + "?";
|
||||
}
|
||||
declarationError(_identifier.location(), errorMessage);
|
||||
m_errorReporter.declarationError(8051_error, _identifier.location(), errorMessage);
|
||||
}
|
||||
else if (declarations.size() == 1)
|
||||
_identifier.annotation().referencedDeclaration = declarations.front();
|
||||
@ -157,7 +157,7 @@ void ReferencesResolver::endVisit(UserDefinedTypeName const& _typeName)
|
||||
Declaration const* declaration = m_resolver.pathFromCurrentScope(_typeName.namePath());
|
||||
if (!declaration)
|
||||
{
|
||||
fatalDeclarationError(_typeName.location(), "Identifier not found or not unique.");
|
||||
m_errorReporter.fatalDeclarationError(7556_error, _typeName.location(), "Identifier not found or not unique.");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -209,14 +209,22 @@ void ReferencesResolver::operator()(yul::Identifier const& _identifier)
|
||||
));
|
||||
if (realName.empty())
|
||||
{
|
||||
declarationError(_identifier.location, "In variable names _slot and _offset can only be used as a suffix.");
|
||||
m_errorReporter.declarationError(
|
||||
9553_error,
|
||||
_identifier.location,
|
||||
"In variable names _slot and _offset can only be used as a suffix."
|
||||
);
|
||||
return;
|
||||
}
|
||||
declarations = m_resolver.nameFromCurrentScope(realName);
|
||||
}
|
||||
if (declarations.size() > 1)
|
||||
{
|
||||
declarationError(_identifier.location, "Multiple matching identifiers. Resolving overloaded identifiers is not supported.");
|
||||
m_errorReporter.declarationError(
|
||||
8827_error,
|
||||
_identifier.location,
|
||||
"Multiple matching identifiers. Resolving overloaded identifiers is not supported."
|
||||
);
|
||||
return;
|
||||
}
|
||||
else if (declarations.size() == 0)
|
||||
@ -224,7 +232,11 @@ void ReferencesResolver::operator()(yul::Identifier const& _identifier)
|
||||
if (auto var = dynamic_cast<VariableDeclaration const*>(declarations.front()))
|
||||
if (var->isLocalVariable() && m_yulInsideFunction)
|
||||
{
|
||||
declarationError(_identifier.location, "Cannot access local Solidity variables from inside an inline assembly function.");
|
||||
m_errorReporter.declarationError(
|
||||
8477_error,
|
||||
_identifier.location,
|
||||
"Cannot access local Solidity variables from inside an inline assembly function."
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -242,7 +254,11 @@ void ReferencesResolver::operator()(yul::VariableDeclaration const& _varDecl)
|
||||
|
||||
string namePrefix = identifier.name.str().substr(0, identifier.name.str().find('.'));
|
||||
if (isSlot || isOffset)
|
||||
declarationError(identifier.location, "In variable declarations _slot and _offset can not be used as a suffix.");
|
||||
m_errorReporter.declarationError(
|
||||
8820_error,
|
||||
identifier.location,
|
||||
"In variable declarations _slot and _offset can not be used as a suffix."
|
||||
);
|
||||
else if (
|
||||
auto declarations = m_resolver.nameFromCurrentScope(namePrefix);
|
||||
!declarations.empty()
|
||||
@ -252,7 +268,8 @@ void ReferencesResolver::operator()(yul::VariableDeclaration const& _varDecl)
|
||||
for (auto const* decl: declarations)
|
||||
ssl.append("The shadowed declaration is here:", decl->location());
|
||||
if (!ssl.infos.empty())
|
||||
declarationError(
|
||||
m_errorReporter.declarationError(
|
||||
6005_error,
|
||||
identifier.location,
|
||||
ssl,
|
||||
namePrefix.size() < identifier.name.str().size() ?
|
||||
@ -266,19 +283,4 @@ void ReferencesResolver::operator()(yul::VariableDeclaration const& _varDecl)
|
||||
visit(*_varDecl.value);
|
||||
}
|
||||
|
||||
void ReferencesResolver::declarationError(SourceLocation const& _location, string const& _description)
|
||||
{
|
||||
m_errorReporter.declarationError(8532_error, _location, _description);
|
||||
}
|
||||
|
||||
void ReferencesResolver::declarationError(SourceLocation const& _location, SecondarySourceLocation const& _ssl, string const& _description)
|
||||
{
|
||||
m_errorReporter.declarationError(3881_error, _location, _ssl, _description);
|
||||
}
|
||||
|
||||
void ReferencesResolver::fatalDeclarationError(SourceLocation const& _location, string const& _description)
|
||||
{
|
||||
m_errorReporter.fatalDeclarationError(6546_error, _location, _description);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -88,15 +88,6 @@ private:
|
||||
void operator()(yul::Identifier const& _identifier) override;
|
||||
void operator()(yul::VariableDeclaration const& _varDecl) override;
|
||||
|
||||
/// Adds a new error to the list of errors.
|
||||
void declarationError(langutil::SourceLocation const& _location, std::string const& _description);
|
||||
|
||||
/// Adds a new error to the list of errors.
|
||||
void declarationError(langutil::SourceLocation const& _location, langutil::SecondarySourceLocation const& _ssl, std::string const& _description);
|
||||
|
||||
/// Adds a new error to the list of errors and throws to abort reference resolving.
|
||||
void fatalDeclarationError(langutil::SourceLocation const& _location, std::string const& _description);
|
||||
|
||||
langutil::ErrorReporter& m_errorReporter;
|
||||
NameAndTypeResolver& m_resolver;
|
||||
langutil::EVMVersion m_evmVersion;
|
||||
|
@ -250,7 +250,7 @@ struct ExpressionAnnotation: ASTAnnotation
|
||||
bool lValueOfOrdinaryAssignment = false;
|
||||
|
||||
/// Types and - if given - names of arguments if the expr. is a function
|
||||
/// that is called, used for overload resoultion
|
||||
/// that is called, used for overload resolution
|
||||
std::optional<FuncCallArguments> arguments;
|
||||
};
|
||||
|
||||
|
@ -185,6 +185,13 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons
|
||||
{
|
||||
solAssert(byteOffsetSize == 0, "Byte offset for array as base type.");
|
||||
auto const& sourceBaseArrayType = dynamic_cast<ArrayType const&>(*sourceBaseType);
|
||||
|
||||
solUnimplementedAssert(
|
||||
_sourceType.location() != DataLocation::CallData ||
|
||||
!_sourceType.isDynamicallyEncoded() ||
|
||||
!sourceBaseArrayType.isDynamicallySized(),
|
||||
"Copying nested calldata dynamic arrays to storage is not implemented in the old code generator."
|
||||
);
|
||||
_context << Instruction::DUP3;
|
||||
if (sourceBaseArrayType.location() == DataLocation::Memory)
|
||||
_context << Instruction::MLOAD;
|
||||
|
@ -974,6 +974,14 @@ void CompilerUtils::convertType(
|
||||
}
|
||||
else
|
||||
{
|
||||
if (auto baseType = dynamic_cast<ArrayType const*>(typeOnStack.baseType()))
|
||||
solUnimplementedAssert(
|
||||
typeOnStack.location() != DataLocation::CallData ||
|
||||
!typeOnStack.isDynamicallyEncoded() ||
|
||||
!baseType->isDynamicallySized(),
|
||||
"Copying nested dynamic calldata arrays to memory is not implemented in the old code generator."
|
||||
);
|
||||
|
||||
m_context << u256(0) << Instruction::SWAP1;
|
||||
// stack: <mem start> <source ref> (variably sized) <length> <counter> <mem data pos>
|
||||
auto repeat = m_context.newTag();
|
||||
|
@ -286,7 +286,7 @@ public:
|
||||
/// Appends code that computes the Keccak-256 hash of the topmost stack element of 32 byte type.
|
||||
void computeHashStatic();
|
||||
|
||||
/// Apppends code that copies the code of the given contract to memory.
|
||||
/// Appends code that copies the code of the given contract to memory.
|
||||
/// Stack pre: Memory position
|
||||
/// Stack post: Updated memory position
|
||||
/// @param creation if true, copies creation code, if false copies runtime code.
|
||||
|
@ -1912,7 +1912,7 @@ void ExpressionCompiler::endVisit(Identifier const& _identifier)
|
||||
// If the identifier is called right away, this code is executed in visit(FunctionCall...), because
|
||||
// we want to avoid having a reference to the runtime function entry point in the
|
||||
// constructor context, since this would force the compiler to include unreferenced
|
||||
// internal functions in the runtime contex.
|
||||
// internal functions in the runtime context.
|
||||
utils().pushCombinedFunctionEntryLabel(functionDef->resolveVirtual(m_context.mostDerivedContract()));
|
||||
else if (auto variable = dynamic_cast<VariableDeclaration const*>(declaration))
|
||||
appendVariable(*variable, static_cast<Expression const&>(_identifier));
|
||||
|
@ -99,3 +99,13 @@ string IRNames::zeroValue(Type const& _type, string const& _variableName)
|
||||
{
|
||||
return "zero_value_for_type_" + _type.identifier() + _variableName;
|
||||
}
|
||||
|
||||
FunctionDefinition const* IRHelpers::referencedFunctionDeclaration(Expression const& _expression)
|
||||
{
|
||||
if (auto memberAccess = dynamic_cast<MemberAccess const*>(&_expression))
|
||||
return dynamic_cast<FunctionDefinition const*>(memberAccess->annotation().referencedDeclaration);
|
||||
else if (auto identifier = dynamic_cast<Identifier const*>(&_expression))
|
||||
return dynamic_cast<FunctionDefinition const*>(identifier->annotation().referencedDeclaration);
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -62,6 +62,11 @@ struct IRNames
|
||||
static std::string zeroValue(Type const& _type, std::string const& _variableName);
|
||||
};
|
||||
|
||||
struct IRHelpers
|
||||
{
|
||||
static FunctionDefinition const* referencedFunctionDeclaration(Expression const& _expression);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
// Overloading std::less() makes it possible to use YulArity as a map key. We could define operator<
|
||||
|
@ -625,15 +625,22 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
||||
arguments.push_back(callArguments[std::distance(callArgumentNames.begin(), it)]);
|
||||
}
|
||||
|
||||
if (auto memberAccess = dynamic_cast<MemberAccess const*>(&_functionCall.expression()))
|
||||
auto memberAccess = dynamic_cast<MemberAccess const*>(&_functionCall.expression());
|
||||
if (memberAccess)
|
||||
{
|
||||
if (auto expressionType = dynamic_cast<TypeType const*>(memberAccess->expression().annotation().type))
|
||||
{
|
||||
solAssert(!functionType->bound(), "");
|
||||
if (auto contractType = dynamic_cast<ContractType const*>(expressionType->actualType()))
|
||||
solUnimplementedAssert(
|
||||
!contractType->contractDefinition().isLibrary() || functionType->kind() == FunctionType::Kind::Internal,
|
||||
"Only internal function calls implemented for libraries"
|
||||
);
|
||||
}
|
||||
}
|
||||
else
|
||||
solAssert(!functionType->bound(), "");
|
||||
|
||||
solUnimplementedAssert(!functionType->bound(), "");
|
||||
switch (functionType->kind())
|
||||
{
|
||||
case FunctionType::Kind::Declaration:
|
||||
@ -641,53 +648,40 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
||||
break;
|
||||
case FunctionType::Kind::Internal:
|
||||
{
|
||||
optional<FunctionDefinition const*> functionDef;
|
||||
if (auto memberAccess = dynamic_cast<MemberAccess const*>(&_functionCall.expression()))
|
||||
{
|
||||
solUnimplementedAssert(!functionType->bound(), "Internal calls to bound functions are not yet implemented for libraries and not allowed for contracts");
|
||||
auto identifier = dynamic_cast<Identifier const*>(&_functionCall.expression());
|
||||
FunctionDefinition const* functionDef = IRHelpers::referencedFunctionDeclaration(_functionCall.expression());
|
||||
|
||||
functionDef = dynamic_cast<FunctionDefinition const*>(memberAccess->annotation().referencedDeclaration);
|
||||
if (functionDef.value() != nullptr)
|
||||
solAssert(functionType->declaration() == *memberAccess->annotation().referencedDeclaration, "");
|
||||
else
|
||||
{
|
||||
solAssert(dynamic_cast<VariableDeclaration const*>(memberAccess->annotation().referencedDeclaration), "");
|
||||
solAssert(!functionType->hasDeclaration(), "");
|
||||
}
|
||||
}
|
||||
else if (auto identifier = dynamic_cast<Identifier const*>(&_functionCall.expression()))
|
||||
if (functionDef)
|
||||
{
|
||||
solAssert(!functionType->bound(), "");
|
||||
solAssert(memberAccess || identifier, "");
|
||||
solAssert(functionType->declaration() == *functionDef, "");
|
||||
|
||||
if (auto unresolvedFunctionDef = dynamic_cast<FunctionDefinition const*>(identifier->annotation().referencedDeclaration))
|
||||
{
|
||||
functionDef = &unresolvedFunctionDef->resolveVirtual(m_context.mostDerivedContract());
|
||||
solAssert(functionType->declaration() == *identifier->annotation().referencedDeclaration, "");
|
||||
}
|
||||
else
|
||||
{
|
||||
functionDef = nullptr;
|
||||
solAssert(dynamic_cast<VariableDeclaration const*>(identifier->annotation().referencedDeclaration), "");
|
||||
solAssert(!functionType->hasDeclaration(), "");
|
||||
}
|
||||
if (identifier)
|
||||
functionDef = &functionDef->resolveVirtual(m_context.mostDerivedContract());
|
||||
|
||||
solAssert(functionDef->isImplemented(), "");
|
||||
}
|
||||
else
|
||||
// Not a simple expression like x or A.x
|
||||
functionDef = nullptr;
|
||||
solAssert(!functionType->hasDeclaration(), "");
|
||||
|
||||
solAssert(functionDef.has_value(), "");
|
||||
solAssert(functionDef.value() == nullptr || functionDef.value()->isImplemented(), "");
|
||||
solAssert(!functionType->takesArbitraryParameters(), "");
|
||||
|
||||
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 (functionType->bound())
|
||||
{
|
||||
solAssert(memberAccess && functionDef, "");
|
||||
solAssert(functionDef->parameters().size() == arguments.size() + 1, "");
|
||||
args += convert(memberAccess->expression(), *functionDef->parameters()[0]->type()).stackSlots();
|
||||
}
|
||||
else
|
||||
solAssert(!functionDef || functionDef->parameters().size() == arguments.size(), "");
|
||||
|
||||
if (functionDef.value() != nullptr)
|
||||
for (size_t i = 0; i < arguments.size(); ++i)
|
||||
args += convert(*arguments[i], *parameterTypes[i]).stackSlots();
|
||||
|
||||
if (functionDef)
|
||||
define(_functionCall) <<
|
||||
m_context.enqueueFunctionForCodeGeneration(*functionDef.value()) <<
|
||||
m_context.enqueueFunctionForCodeGeneration(*functionDef) <<
|
||||
"(" <<
|
||||
joinHumanReadable(args) <<
|
||||
")\n";
|
||||
@ -1237,13 +1231,27 @@ void IRGeneratorForStatements::endVisit(FunctionCallOptions const& _options)
|
||||
void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess)
|
||||
{
|
||||
ASTString const& member = _memberAccess.memberName();
|
||||
if (auto funType = dynamic_cast<FunctionType const*>(_memberAccess.annotation().type))
|
||||
if (funType->bound())
|
||||
{
|
||||
solUnimplementedAssert(false, "");
|
||||
}
|
||||
auto memberFunctionType = dynamic_cast<FunctionType const*>(_memberAccess.annotation().type);
|
||||
Type::Category objectCategory = _memberAccess.expression().annotation().type->category();
|
||||
|
||||
switch (_memberAccess.expression().annotation().type->category())
|
||||
if (memberFunctionType && memberFunctionType->bound())
|
||||
{
|
||||
solAssert((set<Type::Category>{
|
||||
Type::Category::Contract,
|
||||
Type::Category::Bool,
|
||||
Type::Category::Integer,
|
||||
Type::Category::Address,
|
||||
Type::Category::Function,
|
||||
Type::Category::Struct,
|
||||
Type::Category::Enum,
|
||||
Type::Category::Mapping,
|
||||
Type::Category::Array,
|
||||
Type::Category::FixedBytes,
|
||||
}).count(objectCategory) > 0, "");
|
||||
return;
|
||||
}
|
||||
|
||||
switch (objectCategory)
|
||||
{
|
||||
case Type::Category::Contract:
|
||||
{
|
||||
@ -1476,9 +1484,9 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess)
|
||||
{
|
||||
if (auto const* variable = dynamic_cast<VariableDeclaration const*>(_memberAccess.annotation().referencedDeclaration))
|
||||
handleVariableReference(*variable, _memberAccess);
|
||||
else if (auto const* funType = dynamic_cast<FunctionType const*>(_memberAccess.annotation().type))
|
||||
else if (memberFunctionType)
|
||||
{
|
||||
switch (funType->kind())
|
||||
switch (memberFunctionType->kind())
|
||||
{
|
||||
case FunctionType::Kind::Declaration:
|
||||
break;
|
||||
@ -1497,7 +1505,7 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess)
|
||||
break;
|
||||
case FunctionType::Kind::DelegateCall:
|
||||
define(IRVariable(_memberAccess).part("address"), _memberAccess.expression());
|
||||
define(IRVariable(_memberAccess).part("functionIdentifier")) << formatNumber(funType->externalIdentifier()) << "\n";
|
||||
define(IRVariable(_memberAccess).part("functionIdentifier")) << formatNumber(memberFunctionType->externalIdentifier()) << "\n";
|
||||
break;
|
||||
case FunctionType::Kind::External:
|
||||
case FunctionType::Kind::Creation:
|
||||
|
@ -178,7 +178,7 @@ bool isArtifactRequested(Json::Value const& _outputSelection, string const& _art
|
||||
}
|
||||
|
||||
///
|
||||
/// @a _outputSelection is a JSON object containining a two-level hashmap, where the first level is the filename,
|
||||
/// @a _outputSelection is a JSON object containing a two-level hashmap, where the first level is the filename,
|
||||
/// the second level is the contract name and the value is an array of artifact names to be requested for that contract.
|
||||
/// @a _file is the current file
|
||||
/// @a _contract is the current contract
|
||||
@ -229,7 +229,7 @@ bool isBinaryRequested(Json::Value const& _outputSelection)
|
||||
if (!_outputSelection.isObject())
|
||||
return false;
|
||||
|
||||
// This does not inculde "evm.methodIdentifiers" on purpose!
|
||||
// This does not include "evm.methodIdentifiers" on purpose!
|
||||
static vector<string> const outputsThatRequireBinaries{
|
||||
"*",
|
||||
"ir", "irOptimized",
|
||||
|
@ -96,7 +96,10 @@ void DocStringParser::parse(string const& _docString, ErrorReporter& _errorRepor
|
||||
auto tagNameEndPos = firstWhitespaceOrNewline(tagPos, end);
|
||||
if (tagNameEndPos == end)
|
||||
{
|
||||
appendError("End of tag " + string(tagPos, tagNameEndPos) + " not found");
|
||||
m_errorReporter->docstringParsingError(
|
||||
9222_error,
|
||||
"End of tag " + string(tagPos, tagNameEndPos) + " not found"
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -138,7 +141,7 @@ DocStringParser::iter DocStringParser::parseDocTagParam(iter _pos, iter _end)
|
||||
auto nameStartPos = skipWhitespace(_pos, _end);
|
||||
if (nameStartPos == _end)
|
||||
{
|
||||
appendError("No param name given");
|
||||
m_errorReporter->docstringParsingError(3335_error, "No param name given");
|
||||
return _end;
|
||||
}
|
||||
auto nameEndPos = firstNonIdentifier(nameStartPos, _end);
|
||||
@ -149,7 +152,7 @@ DocStringParser::iter DocStringParser::parseDocTagParam(iter _pos, iter _end)
|
||||
|
||||
if (descStartPos == nlPos)
|
||||
{
|
||||
appendError("No description given for param " + paramName);
|
||||
m_errorReporter->docstringParsingError(9942_error, "No description given for param " + paramName);
|
||||
return _end;
|
||||
}
|
||||
|
||||
@ -189,8 +192,3 @@ void DocStringParser::newTag(string const& _tagName)
|
||||
{
|
||||
m_lastTag = &m_docTags.insert(make_pair(_tagName, DocTag()))->second;
|
||||
}
|
||||
|
||||
void DocStringParser::appendError(string const& _description)
|
||||
{
|
||||
m_errorReporter->docstringParsingError(9440_error, _description);
|
||||
}
|
||||
|
@ -58,8 +58,6 @@ private:
|
||||
/// Creates and inserts a new tag and adjusts m_lastTag.
|
||||
void newTag(std::string const& _tagName);
|
||||
|
||||
void appendError(std::string const& _description);
|
||||
|
||||
/// Mapping tag name -> content.
|
||||
std::multimap<std::string, DocTag> m_docTags;
|
||||
DocTag* m_lastTag = nullptr;
|
||||
|
@ -138,4 +138,13 @@ bool validateUTF8(std::string const& _input, size_t& _invalidPosition)
|
||||
return validateUTF8(reinterpret_cast<unsigned char const*>(_input.c_str()), _input.length(), _invalidPosition);
|
||||
}
|
||||
|
||||
size_t numCodepoints(std::string const& _utf8EncodedInput)
|
||||
{
|
||||
size_t codepoint = 0;
|
||||
for (char c: _utf8EncodedInput)
|
||||
codepoint += (c & 0xc0) != 0x80;
|
||||
|
||||
return codepoint;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -38,4 +38,6 @@ inline bool validateUTF8(std::string const& _input)
|
||||
return validateUTF8(_input, invalidPos);
|
||||
}
|
||||
|
||||
size_t numCodepoints(std::string const& _utf8EncodedInput);
|
||||
|
||||
}
|
||||
|
@ -382,15 +382,15 @@ bytes BinaryTransform::operator()(Loop const& _loop)
|
||||
return result;
|
||||
}
|
||||
|
||||
bytes BinaryTransform::operator()(Break const& _break)
|
||||
bytes BinaryTransform::operator()(Branch const& _branch)
|
||||
{
|
||||
return toBytes(Opcode::Br) + encodeLabelIdx(_break.label.name);
|
||||
return toBytes(Opcode::Br) + encodeLabelIdx(_branch.label.name);
|
||||
}
|
||||
|
||||
bytes BinaryTransform::operator()(BreakIf const& _breakIf)
|
||||
bytes BinaryTransform::operator()(BranchIf const& _branchIf)
|
||||
{
|
||||
bytes result = std::visit(*this, *_breakIf.condition);
|
||||
result += toBytes(Opcode::BrIf) + encodeLabelIdx(_breakIf.label.name);
|
||||
bytes result = std::visit(*this, *_branchIf.condition);
|
||||
result += toBytes(Opcode::BrIf) + encodeLabelIdx(_branchIf.label.name);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -48,8 +48,8 @@ public:
|
||||
bytes operator()(wasm::GlobalAssignment const& _assignment);
|
||||
bytes operator()(wasm::If const& _if);
|
||||
bytes operator()(wasm::Loop const& _loop);
|
||||
bytes operator()(wasm::Break const& _break);
|
||||
bytes operator()(wasm::BreakIf const& _break);
|
||||
bytes operator()(wasm::Branch const& _branch);
|
||||
bytes operator()(wasm::BranchIf const& _branchIf);
|
||||
bytes operator()(wasm::Return const& _return);
|
||||
bytes operator()(wasm::Block const& _block);
|
||||
bytes operator()(wasm::FunctionDefinition const& _function);
|
||||
|
@ -121,14 +121,14 @@ string TextTransform::operator()(wasm::Loop const& _loop)
|
||||
return "(loop" + move(label) + "\n" + indented(joinTransformed(_loop.statements, '\n')) + ")\n";
|
||||
}
|
||||
|
||||
string TextTransform::operator()(wasm::Break const& _break)
|
||||
string TextTransform::operator()(wasm::Branch const& _branch)
|
||||
{
|
||||
return "(br $" + _break.label.name + ")\n";
|
||||
return "(br $" + _branch.label.name + ")\n";
|
||||
}
|
||||
|
||||
string TextTransform::operator()(wasm::BreakIf const& _break)
|
||||
string TextTransform::operator()(wasm::BranchIf const& _branchIf)
|
||||
{
|
||||
return "(br_if $" + _break.label.name + " " + visit(*_break.condition) + ")\n";
|
||||
return "(br_if $" + _branchIf.label.name + " " + visit(*_branchIf.condition) + ")\n";
|
||||
}
|
||||
|
||||
string TextTransform::operator()(wasm::Return const&)
|
||||
|
@ -48,9 +48,9 @@ public:
|
||||
std::string operator()(wasm::GlobalAssignment const& _assignment);
|
||||
std::string operator()(wasm::If const& _if);
|
||||
std::string operator()(wasm::Loop const& _loop);
|
||||
std::string operator()(wasm::Break const& _break);
|
||||
std::string operator()(wasm::Branch const& _branch);
|
||||
std::string operator()(wasm::BranchIf const& _branchIf);
|
||||
std::string operator()(wasm::Return const& _return);
|
||||
std::string operator()(wasm::BreakIf const& _break);
|
||||
std::string operator()(wasm::Block const& _block);
|
||||
|
||||
private:
|
||||
|
@ -41,13 +41,13 @@ struct GlobalAssignment;
|
||||
struct Block;
|
||||
struct If;
|
||||
struct Loop;
|
||||
struct Break;
|
||||
struct BreakIf;
|
||||
struct Branch;
|
||||
struct BranchIf;
|
||||
struct Return;
|
||||
using Expression = std::variant<
|
||||
Literal, StringLiteral, LocalVariable, GlobalVariable,
|
||||
FunctionCall, BuiltinCall, LocalAssignment, GlobalAssignment,
|
||||
Block, If, Loop, Break, BreakIf, Return
|
||||
Block, If, Loop, Branch, BranchIf, Return
|
||||
>;
|
||||
|
||||
struct Literal { uint64_t value; };
|
||||
@ -66,9 +66,9 @@ struct If {
|
||||
std::unique_ptr<std::vector<Expression>> elseStatements;
|
||||
};
|
||||
struct Loop { std::string labelName; std::vector<Expression> statements; };
|
||||
struct Break { Label label; };
|
||||
struct Branch { Label label; };
|
||||
struct Return {};
|
||||
struct BreakIf { Label label; std::unique_ptr<Expression> condition; };
|
||||
struct BranchIf { Label label; std::unique_ptr<Expression> condition; };
|
||||
|
||||
struct VariableDeclaration { std::string variableName; };
|
||||
struct GlobalVariableDeclaration { std::string variableName; };
|
||||
|
@ -256,26 +256,26 @@ wasm::Expression WasmCodeTransform::operator()(ForLoop const& _for)
|
||||
wasm::Loop loop;
|
||||
loop.labelName = newLabel();
|
||||
loop.statements = visit(_for.pre.statements);
|
||||
loop.statements.emplace_back(wasm::BreakIf{wasm::Label{breakLabel}, make_unique<wasm::Expression>(
|
||||
loop.statements.emplace_back(wasm::BranchIf{wasm::Label{breakLabel}, make_unique<wasm::Expression>(
|
||||
wasm::BuiltinCall{"i64.eqz", make_vector<wasm::Expression>(
|
||||
visitReturnByValue(*_for.condition)
|
||||
)}
|
||||
)});
|
||||
loop.statements.emplace_back(wasm::Block{continueLabel, visit(_for.body.statements)});
|
||||
loop.statements += visit(_for.post.statements);
|
||||
loop.statements.emplace_back(wasm::Break{wasm::Label{loop.labelName}});
|
||||
loop.statements.emplace_back(wasm::Branch{wasm::Label{loop.labelName}});
|
||||
|
||||
return { wasm::Block{breakLabel, make_vector<wasm::Expression>(move(loop))} };
|
||||
}
|
||||
|
||||
wasm::Expression WasmCodeTransform::operator()(Break const&)
|
||||
{
|
||||
return wasm::Break{wasm::Label{m_breakContinueLabelNames.top().first}};
|
||||
return wasm::Branch{wasm::Label{m_breakContinueLabelNames.top().first}};
|
||||
}
|
||||
|
||||
wasm::Expression WasmCodeTransform::operator()(Continue const&)
|
||||
{
|
||||
return wasm::Break{wasm::Label{m_breakContinueLabelNames.top().second}};
|
||||
return wasm::Branch{wasm::Label{m_breakContinueLabelNames.top().second}};
|
||||
}
|
||||
|
||||
wasm::Expression WasmCodeTransform::operator()(Leave const&)
|
||||
|
@ -11,3 +11,4 @@ compilability
|
||||
errorstring
|
||||
hist
|
||||
otion
|
||||
keypair
|
||||
|
@ -13,7 +13,7 @@ import hashlib
|
||||
from os.path import join, isfile
|
||||
|
||||
def extract_test_cases(path):
|
||||
lines = open(path, mode='r', encoding='utf8').read().splitlines()
|
||||
lines = open(path, encoding="utf8", errors='ignore', mode='r').read().splitlines()
|
||||
|
||||
inside = False
|
||||
delimiter = ''
|
||||
|
@ -6,7 +6,7 @@
|
||||
## You can pass a branch name as argument to this script (which, if no argument is given,
|
||||
## will default to "develop").
|
||||
##
|
||||
## If the gien branch is "release", the resulting package will be uplaoded to
|
||||
## If the given branch is "release", the resulting package will be uploaded to
|
||||
## ethereum/ethereum PPA, or ethereum/ethereum-dev PPA otherwise.
|
||||
##
|
||||
## The gnupg key for "builds@ethereum.org" has to be present in order to sign
|
||||
|
@ -8,7 +8,7 @@ from os.path import join, isfile
|
||||
|
||||
|
||||
def extract_test_cases(path):
|
||||
lines = open(path, 'rb').read().splitlines()
|
||||
lines = open(path, encoding="utf8", errors='ignore', mode='rb').read().splitlines()
|
||||
|
||||
inside = False
|
||||
delimiter = ''
|
||||
|
11
test/cmdlineTests/message_format_utf16/err
Normal file
11
test/cmdlineTests/message_format_utf16/err
Normal file
@ -0,0 +1,11 @@
|
||||
Warning: SPDX license identifier not provided in source file. Before publishing, consider adding a comment containing "SPDX-License-Identifier: <SPDX-License>" to each source file. Use "SPDX-License-Identifier: UNLICENSED" for non-open-source code. Please see https://spdx.org for more information.
|
||||
--> message_format_utf16/input.sol
|
||||
|
||||
Warning: Source file does not specify required compiler version!
|
||||
--> message_format_utf16/input.sol
|
||||
|
||||
Warning: Statement has no effect.
|
||||
--> message_format_utf16/input.sol:2:58:
|
||||
|
|
||||
2 | /* ©©©©ᄅ©©©©© 2017 */ constructor () public { "©©©©ᄅ©©©©©" ; }
|
||||
| ^^^^^^^^^^^^
|
3
test/cmdlineTests/message_format_utf16/input.sol
Normal file
3
test/cmdlineTests/message_format_utf16/input.sol
Normal file
@ -0,0 +1,3 @@
|
||||
contract Foo {
|
||||
/* ©©©©ᄅ©©©©© 2017 */ constructor () public { "©©©©ᄅ©©©©©" ; }
|
||||
}
|
@ -193,7 +193,7 @@ contract ico is safeMath {
|
||||
|
||||
function setICOEthPrice(uint256 value) external {
|
||||
/*
|
||||
Setting of the ICO ETC USD rates which can only be calle by a pre-defined address.
|
||||
Setting of the ICO ETC USD rates which can only be called by a pre-defined address.
|
||||
After this function is completed till the call of the next function (which is at least an exchangeRateDelay array) this rate counts.
|
||||
With this process avoiding the sudden rate changes.
|
||||
|
||||
@ -221,8 +221,8 @@ contract ico is safeMath {
|
||||
/*
|
||||
Closing the ICO.
|
||||
It is only possible when the ICO period passed and only by the owner.
|
||||
The 96% of the whole amount of the token is generated to the address of the fundation.
|
||||
Ethers which are situated in this contract will be sent to the address of the fundation.
|
||||
The 96% of the whole amount of the token is generated to the address of the foundation.
|
||||
Ethers which are situated in this contract will be sent to the address of the foundation.
|
||||
*/
|
||||
require( msg.sender == owner );
|
||||
require( block.number > icoDelay );
|
||||
|
@ -826,8 +826,8 @@ BOOST_AUTO_TEST_CASE(block_deduplicator)
|
||||
Instruction::JUMP,
|
||||
AssemblyItem(Tag, 3)
|
||||
};
|
||||
BlockDeduplicator dedup(input);
|
||||
dedup.deduplicate();
|
||||
BlockDeduplicator deduplicator(input);
|
||||
deduplicator.deduplicate();
|
||||
|
||||
set<u256> pushTags;
|
||||
for (AssemblyItem const& item: input)
|
||||
@ -857,8 +857,8 @@ BOOST_AUTO_TEST_CASE(block_deduplicator_assign_immutable_same)
|
||||
AssemblyItem(PushTag, 1),
|
||||
AssemblyItem(PushTag, 1),
|
||||
} + blocks;
|
||||
BlockDeduplicator dedup(input);
|
||||
dedup.deduplicate();
|
||||
BlockDeduplicator deduplicator(input);
|
||||
deduplicator.deduplicate();
|
||||
BOOST_CHECK_EQUAL_COLLECTIONS(input.begin(), input.end(), output.begin(), output.end());
|
||||
}
|
||||
|
||||
@ -876,8 +876,8 @@ BOOST_AUTO_TEST_CASE(block_deduplicator_assign_immutable_different_value)
|
||||
AssemblyItem{AssignImmutable, 0x1234},
|
||||
Instruction::JUMP
|
||||
};
|
||||
BlockDeduplicator dedup(input);
|
||||
BOOST_CHECK(!dedup.deduplicate());
|
||||
BlockDeduplicator deduplicator(input);
|
||||
BOOST_CHECK(!deduplicator.deduplicate());
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(block_deduplicator_assign_immutable_different_hash)
|
||||
@ -894,8 +894,8 @@ BOOST_AUTO_TEST_CASE(block_deduplicator_assign_immutable_different_hash)
|
||||
AssemblyItem{AssignImmutable, 0xABCD},
|
||||
Instruction::JUMP
|
||||
};
|
||||
BlockDeduplicator dedup(input);
|
||||
BOOST_CHECK(!dedup.deduplicate());
|
||||
BlockDeduplicator deduplicator(input);
|
||||
BOOST_CHECK(!deduplicator.deduplicate());
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(block_deduplicator_loops)
|
||||
@ -920,8 +920,8 @@ BOOST_AUTO_TEST_CASE(block_deduplicator_loops)
|
||||
AssemblyItem(PushTag, 2),
|
||||
Instruction::JUMP,
|
||||
};
|
||||
BlockDeduplicator dedup(input);
|
||||
dedup.deduplicate();
|
||||
BlockDeduplicator deduplicator(input);
|
||||
deduplicator.deduplicate();
|
||||
|
||||
set<u256> pushTags;
|
||||
for (AssemblyItem const& item: input)
|
||||
|
@ -78,6 +78,81 @@ private:
|
||||
|
||||
BOOST_FIXTURE_TEST_SUITE(SolidityNatspecJSON, DocumentationChecker)
|
||||
|
||||
BOOST_AUTO_TEST_CASE(user_empty_natspec_test)
|
||||
{
|
||||
char const* sourceCode = R"(
|
||||
contract test {
|
||||
///
|
||||
///
|
||||
function f() public {
|
||||
}
|
||||
}
|
||||
)";
|
||||
|
||||
char const* natspec = R"(
|
||||
{
|
||||
"methods": {}
|
||||
}
|
||||
)";
|
||||
|
||||
checkNatspec(sourceCode, "test", natspec, true);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(user_newline_break)
|
||||
{
|
||||
char const* sourceCode = R"(
|
||||
contract test {
|
||||
///
|
||||
/// @notice hello
|
||||
|
||||
/// @notice world
|
||||
function f() public {
|
||||
}
|
||||
}
|
||||
)";
|
||||
|
||||
char const* natspec = R"ABCDEF(
|
||||
{
|
||||
"methods": {
|
||||
"f()":
|
||||
{
|
||||
"notice": "world"
|
||||
}
|
||||
}
|
||||
}
|
||||
)ABCDEF";
|
||||
|
||||
checkNatspec(sourceCode, "test", natspec, true);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(user_multiline_empty_lines)
|
||||
{
|
||||
char const* sourceCode = R"(
|
||||
contract test {
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @notice hello world
|
||||
*/
|
||||
function f() public {
|
||||
}
|
||||
}
|
||||
)";
|
||||
|
||||
char const* natspec = R"ABCDEF(
|
||||
{
|
||||
"methods": {
|
||||
"f()": {
|
||||
"notice": "hello world"
|
||||
}
|
||||
}
|
||||
}
|
||||
)ABCDEF";
|
||||
|
||||
checkNatspec(sourceCode, "test", natspec, true);
|
||||
}
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE(user_basic_test)
|
||||
{
|
||||
char const* sourceCode = R"(
|
||||
|
@ -361,7 +361,7 @@ BOOST_AUTO_TEST_CASE(multiline_documentation_comments_parsed)
|
||||
BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier);
|
||||
BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier);
|
||||
BOOST_CHECK_EQUAL(scanner.next(), Token::EOS);
|
||||
BOOST_CHECK_EQUAL(scanner.currentCommentLiteral(), "Send $(value / 1000) chocolates to the user");
|
||||
BOOST_CHECK_EQUAL(scanner.currentCommentLiteral(), " Send $(value / 1000) chocolates to the user");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(multiline_documentation_no_stars)
|
||||
@ -385,7 +385,7 @@ BOOST_AUTO_TEST_CASE(multiline_documentation_whitespace_hell)
|
||||
BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier);
|
||||
BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier);
|
||||
BOOST_CHECK_EQUAL(scanner.next(), Token::EOS);
|
||||
BOOST_CHECK_EQUAL(scanner.currentCommentLiteral(), "Send $(value / 1000) chocolates to the user");
|
||||
BOOST_CHECK_EQUAL(scanner.currentCommentLiteral(), " Send $(value / 1000) chocolates to the user");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(comment_before_eos)
|
||||
|
@ -0,0 +1,18 @@
|
||||
library L {
|
||||
function equals(address a, address b) internal pure returns (bool) {
|
||||
return a == b;
|
||||
}
|
||||
}
|
||||
|
||||
contract C {
|
||||
using L for address;
|
||||
|
||||
function foo(address a, address b) public returns (bool) {
|
||||
return a.equals(b);
|
||||
}
|
||||
}
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// foo(address, address): 0x111122223333444455556666777788889999aAaa, 0x111122223333444455556666777788889999aAaa -> true
|
||||
// foo(address, address): 0x111122223333444455556666777788889999aAaa, 0x0000000000000000000000000000000000000000 -> false
|
@ -0,0 +1,21 @@
|
||||
library L {
|
||||
function transfer(address a) internal {}
|
||||
function send(address a) internal {}
|
||||
}
|
||||
|
||||
contract C {
|
||||
using L for address;
|
||||
|
||||
function useTransfer(address a) public {
|
||||
a.transfer();
|
||||
}
|
||||
|
||||
function useSend(address a) public {
|
||||
a.send();
|
||||
}
|
||||
}
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// useTransfer(address): 0x111122223333444455556666777788889999aAaa ->
|
||||
// useSend(address): 0x111122223333444455556666777788889999aAaa ->
|
@ -0,0 +1,19 @@
|
||||
library L {
|
||||
function pop(uint[2] memory a) internal {}
|
||||
function push(uint[2] memory a) internal {}
|
||||
}
|
||||
|
||||
contract C {
|
||||
using L for uint[2];
|
||||
|
||||
function test() public {
|
||||
uint[2] memory input;
|
||||
|
||||
input.push();
|
||||
input.pop();
|
||||
}
|
||||
}
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// test() ->
|
@ -0,0 +1,20 @@
|
||||
library L {
|
||||
function xor(bool a, bool b) internal pure returns (bool) {
|
||||
return a != b;
|
||||
}
|
||||
}
|
||||
|
||||
contract C {
|
||||
using L for bool;
|
||||
|
||||
function foo(bool a, bool b) public returns (bool) {
|
||||
return a.xor(b);
|
||||
}
|
||||
}
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// foo(bool, bool): true, true -> false
|
||||
// foo(bool, bool): true, false -> true
|
||||
// foo(bool, bool): false, true -> true
|
||||
// foo(bool, bool): false, false -> false
|
@ -0,0 +1,21 @@
|
||||
contract E {}
|
||||
|
||||
library L {
|
||||
function foo(E e) internal pure returns (uint) {
|
||||
return 42;
|
||||
}
|
||||
}
|
||||
|
||||
contract C {
|
||||
using L for E;
|
||||
|
||||
function test() public returns (uint) {
|
||||
E e = new E();
|
||||
return e.foo();
|
||||
}
|
||||
}
|
||||
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// test() -> 42
|
@ -0,0 +1,21 @@
|
||||
library L {
|
||||
function at(uint[] memory a, uint i) internal pure returns (uint) {
|
||||
return a[i];
|
||||
}
|
||||
}
|
||||
|
||||
contract C {
|
||||
using L for uint[];
|
||||
|
||||
function secondItem() public returns (uint) {
|
||||
uint[] memory input = new uint[](2);
|
||||
input[0] = 0x11;
|
||||
input[1] = 0x22;
|
||||
|
||||
return input.at(1);
|
||||
}
|
||||
}
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// secondItem() -> 0x22
|
@ -0,0 +1,21 @@
|
||||
library L {
|
||||
enum E { A, B }
|
||||
|
||||
function equals(E a, E b) internal pure returns (bool) {
|
||||
return a == b;
|
||||
}
|
||||
}
|
||||
|
||||
contract C {
|
||||
using L for L.E;
|
||||
|
||||
function equalsA(uint choice) public returns (bool) {
|
||||
L.E x = L.E.A;
|
||||
return x.equals(L.E(choice));
|
||||
}
|
||||
}
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// equalsA(uint256): 0 -> true
|
||||
// equalsA(uint256): 1 -> false
|
@ -0,0 +1,23 @@
|
||||
library L {
|
||||
// NOTE: External function takes up two stack slots
|
||||
function double(function(uint) external pure returns (uint) f, uint x) internal pure returns (uint) {
|
||||
return f(x) * 2;
|
||||
}
|
||||
}
|
||||
|
||||
contract C {
|
||||
using L for function(uint) external pure returns (uint);
|
||||
|
||||
function identity(uint x) external pure returns (uint) {
|
||||
return x;
|
||||
}
|
||||
|
||||
function test(uint value) public returns (uint) {
|
||||
return this.identity.double(value);
|
||||
}
|
||||
}
|
||||
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// test(uint256): 5 -> 10
|
@ -0,0 +1,21 @@
|
||||
library L {
|
||||
function at(uint[2] memory a, uint i) internal pure returns (uint) {
|
||||
return a[i];
|
||||
}
|
||||
}
|
||||
|
||||
contract C {
|
||||
using L for uint[2];
|
||||
|
||||
function secondItem() public returns (uint) {
|
||||
uint[2] memory input;
|
||||
input[0] = 0x11;
|
||||
input[1] = 0x22;
|
||||
|
||||
return input.at(1);
|
||||
}
|
||||
}
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// secondItem() -> 0x22
|
@ -0,0 +1,17 @@
|
||||
library L {
|
||||
function add(bytes2 a, bytes2 b) internal pure returns (bytes2) {
|
||||
return bytes2(uint16(a) + uint16(b));
|
||||
}
|
||||
}
|
||||
|
||||
contract C {
|
||||
using L for bytes2;
|
||||
|
||||
function sum(bytes2 a, bytes2 b) public returns (bytes2) {
|
||||
return a.add(b);
|
||||
}
|
||||
}
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// sum(bytes2, bytes2): left(0x1100), left(0x0022) -> left(0x1122)
|
@ -0,0 +1,22 @@
|
||||
library L {
|
||||
function selector(function(uint) internal pure returns (uint) f, uint x) internal pure returns (uint) {
|
||||
return f(x) * 2;
|
||||
}
|
||||
}
|
||||
|
||||
contract C {
|
||||
using L for function(uint) internal pure returns (uint);
|
||||
|
||||
function identity(uint x) internal pure returns (uint) {
|
||||
return x;
|
||||
}
|
||||
|
||||
function test(uint value) public returns (uint) {
|
||||
return identity.selector(value);
|
||||
}
|
||||
}
|
||||
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// test(uint256): 5 -> 10
|
@ -0,0 +1,17 @@
|
||||
library L {
|
||||
function add(uint256 a, uint256 b) internal pure returns (uint256) {
|
||||
return a + b;
|
||||
}
|
||||
}
|
||||
|
||||
contract C {
|
||||
using L for uint256;
|
||||
|
||||
function foo(uint256 a, uint256 b) public returns (uint256) {
|
||||
return a.add(b);
|
||||
}
|
||||
}
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// foo(uint256, uint256): 8, 42 -> 50
|
@ -0,0 +1,22 @@
|
||||
interface I {}
|
||||
contract E is I {}
|
||||
|
||||
library L {
|
||||
function foo(I i) internal pure returns (uint) {
|
||||
return 42;
|
||||
}
|
||||
}
|
||||
|
||||
contract C {
|
||||
using L for I;
|
||||
|
||||
function test() public returns (uint) {
|
||||
E e = new E();
|
||||
return I(e).foo();
|
||||
}
|
||||
}
|
||||
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// test() -> 42
|
@ -0,0 +1,22 @@
|
||||
library L {
|
||||
function double(function(uint) internal pure returns (uint) f, uint x) internal pure returns (uint) {
|
||||
return f(x) * 2;
|
||||
}
|
||||
}
|
||||
|
||||
contract C {
|
||||
using L for function(uint) internal pure returns (uint);
|
||||
|
||||
function identity(uint x) internal pure returns (uint) {
|
||||
return x;
|
||||
}
|
||||
|
||||
function test(uint value) public returns (uint) {
|
||||
return identity.double(value);
|
||||
}
|
||||
}
|
||||
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// test(uint256): 5 -> 10
|
@ -0,0 +1,22 @@
|
||||
library L {
|
||||
function at(mapping(uint => uint) storage a, uint i) internal view returns (uint) {
|
||||
return a[i];
|
||||
}
|
||||
}
|
||||
|
||||
contract C {
|
||||
using L for mapping(uint => uint);
|
||||
|
||||
mapping(uint => uint) map;
|
||||
|
||||
function mapValue(uint a) public returns (uint) {
|
||||
map[42] = 0x24;
|
||||
map[66] = 0x66;
|
||||
|
||||
return map.at(a);
|
||||
}
|
||||
}
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// mapValue(uint256): 42 -> 0x24
|
@ -0,0 +1,18 @@
|
||||
library L {
|
||||
function at(string memory a, uint i) internal pure returns (uint8) {
|
||||
return uint8(bytes(a)[i]);
|
||||
}
|
||||
}
|
||||
|
||||
contract C {
|
||||
using L for string;
|
||||
|
||||
function secondChar() public returns (uint8) {
|
||||
string memory input = "abc";
|
||||
return input.at(1);
|
||||
}
|
||||
}
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// secondChar() -> 98
|
17
test/libsolidity/smtCheckerTests/types/unused_mapping.sol
Normal file
17
test/libsolidity/smtCheckerTests/types/unused_mapping.sol
Normal file
@ -0,0 +1,17 @@
|
||||
pragma experimental SMTChecker;
|
||||
|
||||
contract C {
|
||||
uint x;
|
||||
uint y;
|
||||
mapping (address => bool) public never_used;
|
||||
|
||||
function inc() public {
|
||||
require(x < 10);
|
||||
require(y < 10);
|
||||
|
||||
if(x == 0) x = 0; // noop state var read
|
||||
x++;
|
||||
y++;
|
||||
assert(y == x);
|
||||
}
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
contract Test {
|
||||
struct shouldBug {
|
||||
bytes[2] deadly;
|
||||
}
|
||||
function killer(bytes[2] calldata weapon) pure external {
|
||||
shouldBug(weapon);
|
||||
}
|
||||
}
|
||||
|
||||
// ----
|
||||
// UnimplementedFeatureError: Copying nested dynamic calldata arrays to memory is not implemented in the old code generator.
|
@ -0,0 +1,13 @@
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
contract Test {
|
||||
struct shouldBug {
|
||||
uint256[][2] deadly;
|
||||
}
|
||||
function killer(uint256[][2] calldata weapon) pure external {
|
||||
shouldBug(weapon);
|
||||
}
|
||||
}
|
||||
|
||||
// ----
|
||||
// UnimplementedFeatureError: Copying nested dynamic calldata arrays to memory is not implemented in the old code generator.
|
@ -0,0 +1,13 @@
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
contract Test {
|
||||
struct shouldBug {
|
||||
uint256[][] deadly;
|
||||
}
|
||||
function killer(uint256[][] calldata weapon) pure external {
|
||||
shouldBug(weapon);
|
||||
}
|
||||
}
|
||||
|
||||
// ----
|
||||
// UnimplementedFeatureError: Copying nested dynamic calldata arrays to memory is not implemented in the old code generator.
|
@ -0,0 +1,9 @@
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
contract C {
|
||||
uint[][2] tmp_i;
|
||||
function i(uint[][2] calldata s) external { tmp_i = s; }
|
||||
}
|
||||
|
||||
// ----
|
||||
// UnimplementedFeatureError: Copying nested calldata dynamic arrays to storage is not implemented in the old code generator.
|
@ -0,0 +1,9 @@
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
contract C {
|
||||
uint[][] tmp_i;
|
||||
function i(uint[][] calldata s) external { tmp_i = s; }
|
||||
}
|
||||
|
||||
// ----
|
||||
// UnimplementedFeatureError: Copying nested calldata dynamic arrays to storage is not implemented in the old code generator.
|
@ -0,0 +1,7 @@
|
||||
contract C {
|
||||
///
|
||||
///
|
||||
function vote(uint id) public {
|
||||
}
|
||||
}
|
||||
// ----
|
Loading…
Reference in New Issue
Block a user