Merge remote-tracking branch 'origin/develop' into breaking

This commit is contained in:
chriseth 2020-05-26 10:11:23 +02:00
commit 69a028b49c
65 changed files with 763 additions and 205 deletions

View File

@ -531,7 +531,7 @@ jobs:
xcode: "11.0.0" xcode: "11.0.0"
environment: environment:
TERM: xterm TERM: xterm
CMAKE_BUILD_TYPE: Debug CMAKE_BUILD_TYPE: Release
steps: steps:
- checkout - checkout
- restore_cache: - restore_cache:

View File

@ -30,7 +30,9 @@ Compiler Features:
Bugfixes: Bugfixes:
* Optimizer: Fixed a bug in BlockDeDuplicator. * Optimizer: Fixed a bug in BlockDeDuplicator.
* Type Checker: Disallow assignments to storage variables of type ``mapping``. * 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) ### 0.6.8 (2020-05-14)

View File

@ -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. 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 * 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`` * Add ``virtual`` to every non-interface function you intend to override. Add ``virtual``
to all functions without implementation outside interfaces. For single inheritance, add to all functions without implementation outside interfaces. For single inheritance, add

View File

@ -435,10 +435,10 @@ map<u256, u256> Assembly::optimiseInternal(
// This only modifies PushTags, we have to run again to actually remove code. // This only modifies PushTags, we have to run again to actually remove code.
if (_settings.runDeduplicate) if (_settings.runDeduplicate)
{ {
BlockDeduplicator dedup{m_items}; BlockDeduplicator deduplicator{m_items};
if (dedup.deduplicate()) if (deduplicator.deduplicate())
{ {
for (auto const& replacement: dedup.replacedTags()) for (auto const& replacement: deduplicator.replacedTags())
{ {
assertThrow( assertThrow(
replacement.first <= size_t(-1) && replacement.second <= size_t(-1), replacement.first <= size_t(-1) && replacement.second <= size_t(-1),

View File

@ -267,10 +267,13 @@ bool Scanner::skipWhitespace()
return sourcePos() != startPosition; return sourcePos() != startPosition;
} }
void Scanner::skipWhitespaceExceptUnicodeLinebreak() bool Scanner::skipWhitespaceExceptUnicodeLinebreak()
{ {
int const startPosition = sourcePos();
while (isWhiteSpace(m_char) && !isUnicodeLinebreak()) while (isWhiteSpace(m_char) && !isUnicodeLinebreak())
advance(); advance();
// Return whether or not we skipped any characters.
return sourcePos() != startPosition;
} }
Token Scanner::skipSingleLineComment() Token Scanner::skipSingleLineComment()
@ -321,7 +324,7 @@ int Scanner::scanSingleLineDocComment()
{ {
// Check if next line is also a single-line comment. // Check if next line is also a single-line comment.
// If any whitespaces were skipped, use source position before. // If any whitespaces were skipped, use source position before.
if (!skipWhitespace()) if (!skipWhitespaceExceptUnicodeLinebreak())
endPosition = m_source->position(); endPosition = m_source->position();
if (!m_source->isPastEndOfInput(3) && if (!m_source->isPastEndOfInput(3) &&
@ -329,8 +332,10 @@ int Scanner::scanSingleLineDocComment()
m_source->get(1) == '/' && m_source->get(1) == '/' &&
m_source->get(2) == '/') m_source->get(2) == '/')
{ {
addCommentLiteralChar('\n');
m_char = m_source->advanceAndGet(3); m_char = m_source->advanceAndGet(3);
if (atEndOfLine())
continue;
addCommentLiteralChar('\n');
} }
else else
break; // next line is not a documentation comment, we are done 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) != '/') else if (!m_source->isPastEndOfInput(1) && m_source->get(0) == '*' && m_source->get(1) != '/')
{ // skip first '*' in subsequent lines { // skip first '*' in subsequent lines
m_char = m_source->advanceAndGet(1);
if (atEndOfLine()) // ignores empty lines
continue;
if (charsAdded) if (charsAdded)
addCommentLiteralChar('\n'); addCommentLiteralChar('\n'); // corresponds to the end of previous line
m_char = m_source->advanceAndGet(2);
} }
else if (!m_source->isPastEndOfInput(1) && m_source->get(0) == '*' && m_source->get(1) == '/') 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 { // if after newline the comment ends, don't insert the newline

View File

@ -214,7 +214,7 @@ private:
/// Skips all whitespace and @returns true if something was skipped. /// Skips all whitespace and @returns true if something was skipped.
bool skipWhitespace(); bool skipWhitespace();
/// Skips all whitespace that are neither '\r' nor '\n'. /// Skips all whitespace that are neither '\r' nor '\n'.
void skipWhitespaceExceptUnicodeLinebreak(); bool skipWhitespaceExceptUnicodeLinebreak();
Token skipSingleLineComment(); Token skipSingleLineComment();
Token skipMultiLineComment(); Token skipMultiLineComment();

View File

@ -21,6 +21,7 @@
#include <liblangutil/SourceReferenceFormatterHuman.h> #include <liblangutil/SourceReferenceFormatterHuman.h>
#include <liblangutil/Scanner.h> #include <liblangutil/Scanner.h>
#include <liblangutil/Exceptions.h> #include <liblangutil/Exceptions.h>
#include <libsolutil/UTF8.h>
#include <iomanip> #include <iomanip>
using namespace std; using namespace std;
@ -103,12 +104,13 @@ void SourceReferenceFormatterHuman::printSourceLocation(SourceReference const& _
m_stream << leftpad << ' '; m_stream << leftpad << ' ';
frameColored() << '|'; frameColored() << '|';
m_stream << ' '; m_stream << ' ';
for_each( for_each(
_ref.text.cbegin(), _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' : ' '); } [this](char ch) { m_stream << (ch == '\t' ? '\t' : ' '); }
); );
diagColored() << string(locationLength, '^'); diagColored() << string(numCodepoints(_ref.text.substr(_ref.startColumn, locationLength)), '^');
m_stream << '\n'; m_stream << '\n';
} }
else else

View File

@ -50,7 +50,8 @@ bool DeclarationTypeChecker::visit(ElementaryTypeName const& _typeName)
_typeName.annotation().type = TypeProvider::address(); _typeName.annotation().type = TypeProvider::address();
break; break;
default: default:
typeError( m_errorReporter.typeError(
2311_error,
_typeName.location(), _typeName.location(),
"Address types can only be payable or non-payable." "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) auto visitor = [&](StructDefinition const& _struct, auto& _cycleDetector, size_t _depth)
{ {
if (_depth >= 256) 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()) 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) 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; return false;
} }
@ -145,9 +150,14 @@ void DeclarationTypeChecker::endVisit(UserDefinedTypeName const& _typeName)
else else
{ {
_typeName.annotation().type = TypeProvider::emptyTuple(); _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) bool DeclarationTypeChecker::visit(FunctionTypeName const& _typeName)
{ {
if (_typeName.annotation().type) if (_typeName.annotation().type)
@ -165,18 +175,27 @@ bool DeclarationTypeChecker::visit(FunctionTypeName const& _typeName)
case Visibility::External: case Visibility::External:
break; break;
default: 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; return false;
} }
if (_typeName.isPayable() && _typeName.visibility() != Visibility::External) 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; return false;
} }
_typeName.annotation().type = TypeProvider::function(_typeName); _typeName.annotation().type = TypeProvider::function(_typeName);
return false; return false;
} }
void DeclarationTypeChecker::endVisit(Mapping const& _mapping) void DeclarationTypeChecker::endVisit(Mapping const& _mapping)
{ {
if (_mapping.annotation().type) if (_mapping.annotation().type)
@ -226,7 +245,11 @@ void DeclarationTypeChecker::endVisit(ArrayTypeName const& _typeName)
return; return;
} }
if (baseType->storageBytes() == 0) 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()) if (Expression const* length = _typeName.length())
{ {
TypePointer& lengthTypeGeneric = length->annotation().type; TypePointer& lengthTypeGeneric = length->annotation().type;
@ -235,13 +258,17 @@ void DeclarationTypeChecker::endVisit(ArrayTypeName const& _typeName)
RationalNumberType const* lengthType = dynamic_cast<RationalNumberType const*>(lengthTypeGeneric); RationalNumberType const* lengthType = dynamic_cast<RationalNumberType const*>(lengthTypeGeneric);
u256 lengthValue = 0; u256 lengthValue = 0;
if (!lengthType || !lengthType->mobileType()) 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()) 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()) 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()) 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 else
lengthValue = lengthType->literalValue(nullptr); lengthValue = lengthType->literalValue(nullptr);
_typeName.annotation().type = TypeProvider::array(DataLocation::Storage, baseType, lengthValue); _typeName.annotation().type = TypeProvider::array(DataLocation::Storage, baseType, lengthValue);
@ -249,15 +276,24 @@ void DeclarationTypeChecker::endVisit(ArrayTypeName const& _typeName)
else else
_typeName.annotation().type = TypeProvider::array(DataLocation::Storage, baseType); _typeName.annotation().type = TypeProvider::array(DataLocation::Storage, baseType);
} }
void DeclarationTypeChecker::endVisit(VariableDeclaration const& _variable) void DeclarationTypeChecker::endVisit(VariableDeclaration const& _variable)
{ {
if (_variable.annotation().type) if (_variable.annotation().type)
return; return;
if (_variable.isConstant() && !_variable.isStateVariable()) 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()) 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()) if (!_variable.typeName())
{ {
@ -309,7 +345,7 @@ void DeclarationTypeChecker::endVisit(VariableDeclaration const& _variable)
errorString += " for variable"; errorString += " for variable";
} }
errorString += ", but " + locationToString(varLoc) + " was given."; errorString += ", but " + locationToString(varLoc) + " was given.";
typeError(_variable.location(), errorString); m_errorReporter.typeError(6160_error, _variable.location(), errorString);
solAssert(!allowedDataLocations.empty(), ""); solAssert(!allowedDataLocations.empty(), "");
varLoc = *allowedDataLocations.begin(); 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) bool DeclarationTypeChecker::check(ASTNode const& _node)
{ {
unsigned errorCount = m_errorReporter.errorCount(); auto watcher = m_errorReporter.errorWatcher();
_node.accept(*this); _node.accept(*this);
return m_errorReporter.errorCount() == errorCount; return watcher.ok();
} }

View File

@ -59,15 +59,6 @@ private:
void endVisit(VariableDeclaration const& _variable) override; void endVisit(VariableDeclaration const& _variable) override;
bool visit(StructDefinition const& _struct) 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::ErrorReporter& m_errorReporter;
langutil::EVMVersion m_evmVersion; langutil::EVMVersion m_evmVersion;
bool m_insideFunctionType = false; bool m_insideFunctionType = false;

View File

@ -68,14 +68,16 @@ bool DocStringAnalyser::visit(VariableDeclaration const& _variable)
parseDocStrings(_variable, _variable.annotation(), validPublicTags, "non-public state variables"); parseDocStrings(_variable, _variable.annotation(), validPublicTags, "non-public state variables");
if (_variable.annotation().docTags.count("notice") > 0) if (_variable.annotation().docTags.count("notice") > 0)
m_errorReporter.warning( m_errorReporter.warning(
9098_error, _variable.documentation()->location(), 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." "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) if (_variable.annotation().docTags.count("title") > 0 || _variable.annotation().docTags.count("author") > 0)
m_errorReporter.warning( m_errorReporter.warning(
4822_error, _variable.documentation()->location(), 8532_error, _variable.documentation()->location(),
"Documentation tag @title and @author is only allowed on contract definitions. It will be disallowed in 0.7.0." "Documentation tag @title and @author is only allowed on contract definitions. "
"It will be disallowed in 0.7.0."
); );
} }
return false; return false;
@ -110,7 +112,8 @@ void DocStringAnalyser::checkParameters(
auto paramRange = _annotation.docTags.equal_range("param"); auto paramRange = _annotation.docTags.equal_range("param");
for (auto i = paramRange.first; i != paramRange.second; ++i) for (auto i = paramRange.first; i != paramRange.second; ++i)
if (!validParams.count(i->second.paramName)) if (!validParams.count(i->second.paramName))
appendError( m_errorReporter.docstringParsingError(
3881_error,
_node.documentation()->location(), _node.documentation()->location(),
"Documented parameter \"" + "Documented parameter \"" +
i->second.paramName + i->second.paramName +
@ -159,7 +162,8 @@ void DocStringAnalyser::parseDocStrings(
for (auto const& docTag: _annotation.docTags) for (auto const& docTag: _annotation.docTags)
{ {
if (!_validTags.count(docTag.first)) if (!_validTags.count(docTag.first))
appendError( m_errorReporter.docstringParsingError(
6546_error,
_node.documentation()->location(), _node.documentation()->location(),
"Documentation tag @" + docTag.first + " not valid for " + _nodeName + "." "Documentation tag @" + docTag.first + " not valid for " + _nodeName + "."
); );
@ -170,12 +174,14 @@ void DocStringAnalyser::parseDocStrings(
if (auto* varDecl = dynamic_cast<VariableDeclaration const*>(&_node)) if (auto* varDecl = dynamic_cast<VariableDeclaration const*>(&_node))
{ {
if (!varDecl->isPublic()) if (!varDecl->isPublic())
appendError( m_errorReporter.docstringParsingError(
9440_error,
_node.documentation()->location(), _node.documentation()->location(),
"Documentation tag \"@" + docTag.first + "\" is only allowed on public state-variables." "Documentation tag \"@" + docTag.first + "\" is only allowed on public state-variables."
); );
if (returnTagsVisited > 1) if (returnTagsVisited > 1)
appendError( m_errorReporter.docstringParsingError(
5256_error,
_node.documentation()->location(), _node.documentation()->location(),
"Documentation tag \"@" + docTag.first + "\" is only allowed once on state-variables." "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")); string firstWord = content.substr(0, content.find_first_of(" \t"));
if (returnTagsVisited > function->returnParameters().size()) if (returnTagsVisited > function->returnParameters().size())
appendError( m_errorReporter.docstringParsingError(
2604_error,
_node.documentation()->location(), _node.documentation()->location(),
"Documentation tag \"@" + docTag.first + " " + docTag.second.content + "\"" + "Documentation tag \"@" + docTag.first + " " + docTag.second.content + "\"" +
" exceeds the number of return parameters." " exceeds the number of return parameters."
@ -195,7 +202,8 @@ void DocStringAnalyser::parseDocStrings(
{ {
auto parameter = function->returnParameters().at(returnTagsVisited - 1); auto parameter = function->returnParameters().at(returnTagsVisited - 1);
if (!parameter->name().empty() && parameter->name() != firstWord) if (!parameter->name().empty() && parameter->name() != firstWord)
appendError( m_errorReporter.docstringParsingError(
5856_error,
_node.documentation()->location(), _node.documentation()->location(),
"Documentation tag \"@" + docTag.first + " " + docTag.second.content + "\"" + "Documentation tag \"@" + docTag.first + " " + docTag.second.content + "\"" +
" does not contain the name of its return parameter." " 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);
}

View File

@ -81,8 +81,6 @@ private:
std::string const& _nodeName std::string const& _nodeName
); );
void appendError(langutil::SourceLocation const& _location, std::string const& _description);
langutil::ErrorReporter& m_errorReporter; langutil::ErrorReporter& m_errorReporter;
}; };

View File

@ -119,7 +119,7 @@ bool ReferencesResolver::visit(Identifier const& _identifier)
else else
errorMessage += " Did you mean " + std::move(suggestions) + "?"; errorMessage += " Did you mean " + std::move(suggestions) + "?";
} }
declarationError(_identifier.location(), errorMessage); m_errorReporter.declarationError(8051_error, _identifier.location(), errorMessage);
} }
else if (declarations.size() == 1) else if (declarations.size() == 1)
_identifier.annotation().referencedDeclaration = declarations.front(); _identifier.annotation().referencedDeclaration = declarations.front();
@ -157,7 +157,7 @@ void ReferencesResolver::endVisit(UserDefinedTypeName const& _typeName)
Declaration const* declaration = m_resolver.pathFromCurrentScope(_typeName.namePath()); Declaration const* declaration = m_resolver.pathFromCurrentScope(_typeName.namePath());
if (!declaration) 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; return;
} }
@ -209,14 +209,22 @@ void ReferencesResolver::operator()(yul::Identifier const& _identifier)
)); ));
if (realName.empty()) 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; return;
} }
declarations = m_resolver.nameFromCurrentScope(realName); declarations = m_resolver.nameFromCurrentScope(realName);
} }
if (declarations.size() > 1) 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; return;
} }
else if (declarations.size() == 0) 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 (auto var = dynamic_cast<VariableDeclaration const*>(declarations.front()))
if (var->isLocalVariable() && m_yulInsideFunction) 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; return;
} }
@ -242,7 +254,11 @@ void ReferencesResolver::operator()(yul::VariableDeclaration const& _varDecl)
string namePrefix = identifier.name.str().substr(0, identifier.name.str().find('.')); string namePrefix = identifier.name.str().substr(0, identifier.name.str().find('.'));
if (isSlot || isOffset) 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 ( else if (
auto declarations = m_resolver.nameFromCurrentScope(namePrefix); auto declarations = m_resolver.nameFromCurrentScope(namePrefix);
!declarations.empty() !declarations.empty()
@ -252,7 +268,8 @@ void ReferencesResolver::operator()(yul::VariableDeclaration const& _varDecl)
for (auto const* decl: declarations) for (auto const* decl: declarations)
ssl.append("The shadowed declaration is here:", decl->location()); ssl.append("The shadowed declaration is here:", decl->location());
if (!ssl.infos.empty()) if (!ssl.infos.empty())
declarationError( m_errorReporter.declarationError(
6005_error,
identifier.location, identifier.location,
ssl, ssl,
namePrefix.size() < identifier.name.str().size() ? namePrefix.size() < identifier.name.str().size() ?
@ -266,19 +283,4 @@ void ReferencesResolver::operator()(yul::VariableDeclaration const& _varDecl)
visit(*_varDecl.value); 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);
}
} }

View File

@ -88,15 +88,6 @@ private:
void operator()(yul::Identifier const& _identifier) override; void operator()(yul::Identifier const& _identifier) override;
void operator()(yul::VariableDeclaration const& _varDecl) 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; langutil::ErrorReporter& m_errorReporter;
NameAndTypeResolver& m_resolver; NameAndTypeResolver& m_resolver;
langutil::EVMVersion m_evmVersion; langutil::EVMVersion m_evmVersion;

View File

@ -250,7 +250,7 @@ struct ExpressionAnnotation: ASTAnnotation
bool lValueOfOrdinaryAssignment = false; bool lValueOfOrdinaryAssignment = false;
/// Types and - if given - names of arguments if the expr. is a function /// 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; std::optional<FuncCallArguments> arguments;
}; };

View File

@ -185,6 +185,13 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons
{ {
solAssert(byteOffsetSize == 0, "Byte offset for array as base type."); solAssert(byteOffsetSize == 0, "Byte offset for array as base type.");
auto const& sourceBaseArrayType = dynamic_cast<ArrayType const&>(*sourceBaseType); 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; _context << Instruction::DUP3;
if (sourceBaseArrayType.location() == DataLocation::Memory) if (sourceBaseArrayType.location() == DataLocation::Memory)
_context << Instruction::MLOAD; _context << Instruction::MLOAD;

View File

@ -974,6 +974,14 @@ void CompilerUtils::convertType(
} }
else 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; m_context << u256(0) << Instruction::SWAP1;
// stack: <mem start> <source ref> (variably sized) <length> <counter> <mem data pos> // stack: <mem start> <source ref> (variably sized) <length> <counter> <mem data pos>
auto repeat = m_context.newTag(); auto repeat = m_context.newTag();

View File

@ -286,7 +286,7 @@ public:
/// Appends code that computes the Keccak-256 hash of the topmost stack element of 32 byte type. /// Appends code that computes the Keccak-256 hash of the topmost stack element of 32 byte type.
void computeHashStatic(); 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 pre: Memory position
/// Stack post: Updated memory position /// Stack post: Updated memory position
/// @param creation if true, copies creation code, if false copies runtime code. /// @param creation if true, copies creation code, if false copies runtime code.

View File

@ -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 // 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 // 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 // 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())); utils().pushCombinedFunctionEntryLabel(functionDef->resolveVirtual(m_context.mostDerivedContract()));
else if (auto variable = dynamic_cast<VariableDeclaration const*>(declaration)) else if (auto variable = dynamic_cast<VariableDeclaration const*>(declaration))
appendVariable(*variable, static_cast<Expression const&>(_identifier)); appendVariable(*variable, static_cast<Expression const&>(_identifier));

View File

@ -99,3 +99,13 @@ string IRNames::zeroValue(Type const& _type, string const& _variableName)
{ {
return "zero_value_for_type_" + _type.identifier() + _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;
}

View File

@ -62,6 +62,11 @@ struct IRNames
static std::string zeroValue(Type const& _type, std::string const& _variableName); 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< // Overloading std::less() makes it possible to use YulArity as a map key. We could define operator<

View File

@ -625,15 +625,22 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
arguments.push_back(callArguments[std::distance(callArgumentNames.begin(), it)]); 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)) if (auto expressionType = dynamic_cast<TypeType const*>(memberAccess->expression().annotation().type))
{
solAssert(!functionType->bound(), "");
if (auto contractType = dynamic_cast<ContractType const*>(expressionType->actualType())) if (auto contractType = dynamic_cast<ContractType const*>(expressionType->actualType()))
solUnimplementedAssert( solUnimplementedAssert(
!contractType->contractDefinition().isLibrary() || functionType->kind() == FunctionType::Kind::Internal, !contractType->contractDefinition().isLibrary() || functionType->kind() == FunctionType::Kind::Internal,
"Only internal function calls implemented for libraries" "Only internal function calls implemented for libraries"
); );
}
}
else
solAssert(!functionType->bound(), "");
solUnimplementedAssert(!functionType->bound(), "");
switch (functionType->kind()) switch (functionType->kind())
{ {
case FunctionType::Kind::Declaration: case FunctionType::Kind::Declaration:
@ -641,53 +648,40 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
break; break;
case FunctionType::Kind::Internal: case FunctionType::Kind::Internal:
{ {
optional<FunctionDefinition const*> functionDef; auto identifier = dynamic_cast<Identifier const*>(&_functionCall.expression());
if (auto memberAccess = dynamic_cast<MemberAccess const*>(&_functionCall.expression())) FunctionDefinition const* functionDef = IRHelpers::referencedFunctionDeclaration(_functionCall.expression());
{
solUnimplementedAssert(!functionType->bound(), "Internal calls to bound functions are not yet implemented for libraries and not allowed for contracts");
functionDef = dynamic_cast<FunctionDefinition const*>(memberAccess->annotation().referencedDeclaration); if (functionDef)
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()))
{ {
solAssert(!functionType->bound(), ""); solAssert(memberAccess || identifier, "");
solAssert(functionType->declaration() == *functionDef, "");
if (auto unresolvedFunctionDef = dynamic_cast<FunctionDefinition const*>(identifier->annotation().referencedDeclaration)) if (identifier)
{ functionDef = &functionDef->resolveVirtual(m_context.mostDerivedContract());
functionDef = &unresolvedFunctionDef->resolveVirtual(m_context.mostDerivedContract());
solAssert(functionType->declaration() == *identifier->annotation().referencedDeclaration, ""); solAssert(functionDef->isImplemented(), "");
}
else
{
functionDef = nullptr;
solAssert(dynamic_cast<VariableDeclaration const*>(identifier->annotation().referencedDeclaration), "");
solAssert(!functionType->hasDeclaration(), "");
}
} }
else else
// Not a simple expression like x or A.x solAssert(!functionType->hasDeclaration(), "");
functionDef = nullptr;
solAssert(functionDef.has_value(), ""); solAssert(!functionType->takesArbitraryParameters(), "");
solAssert(functionDef.value() == nullptr || functionDef.value()->isImplemented(), "");
vector<string> args; vector<string> args;
for (size_t i = 0; i < arguments.size(); ++i) if (functionType->bound())
if (functionType->takesArbitraryParameters()) {
args += IRVariable(*arguments[i]).stackSlots(); solAssert(memberAccess && functionDef, "");
else solAssert(functionDef->parameters().size() == arguments.size() + 1, "");
args += convert(*arguments[i], *parameterTypes[i]).stackSlots(); 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) << define(_functionCall) <<
m_context.enqueueFunctionForCodeGeneration(*functionDef.value()) << m_context.enqueueFunctionForCodeGeneration(*functionDef) <<
"(" << "(" <<
joinHumanReadable(args) << joinHumanReadable(args) <<
")\n"; ")\n";
@ -1237,13 +1231,27 @@ void IRGeneratorForStatements::endVisit(FunctionCallOptions const& _options)
void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess) void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess)
{ {
ASTString const& member = _memberAccess.memberName(); ASTString const& member = _memberAccess.memberName();
if (auto funType = dynamic_cast<FunctionType const*>(_memberAccess.annotation().type)) auto memberFunctionType = dynamic_cast<FunctionType const*>(_memberAccess.annotation().type);
if (funType->bound()) Type::Category objectCategory = _memberAccess.expression().annotation().type->category();
{
solUnimplementedAssert(false, "");
}
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: 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)) if (auto const* variable = dynamic_cast<VariableDeclaration const*>(_memberAccess.annotation().referencedDeclaration))
handleVariableReference(*variable, _memberAccess); 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: case FunctionType::Kind::Declaration:
break; break;
@ -1497,7 +1505,7 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess)
break; break;
case FunctionType::Kind::DelegateCall: case FunctionType::Kind::DelegateCall:
define(IRVariable(_memberAccess).part("address"), _memberAccess.expression()); 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; break;
case FunctionType::Kind::External: case FunctionType::Kind::External:
case FunctionType::Kind::Creation: case FunctionType::Kind::Creation:

View File

@ -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. /// 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 _file is the current file
/// @a _contract is the current contract /// @a _contract is the current contract
@ -229,7 +229,7 @@ bool isBinaryRequested(Json::Value const& _outputSelection)
if (!_outputSelection.isObject()) if (!_outputSelection.isObject())
return false; return false;
// This does not inculde "evm.methodIdentifiers" on purpose! // This does not include "evm.methodIdentifiers" on purpose!
static vector<string> const outputsThatRequireBinaries{ static vector<string> const outputsThatRequireBinaries{
"*", "*",
"ir", "irOptimized", "ir", "irOptimized",

View File

@ -96,7 +96,10 @@ void DocStringParser::parse(string const& _docString, ErrorReporter& _errorRepor
auto tagNameEndPos = firstWhitespaceOrNewline(tagPos, end); auto tagNameEndPos = firstWhitespaceOrNewline(tagPos, end);
if (tagNameEndPos == 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; break;
} }
@ -138,7 +141,7 @@ DocStringParser::iter DocStringParser::parseDocTagParam(iter _pos, iter _end)
auto nameStartPos = skipWhitespace(_pos, _end); auto nameStartPos = skipWhitespace(_pos, _end);
if (nameStartPos == _end) if (nameStartPos == _end)
{ {
appendError("No param name given"); m_errorReporter->docstringParsingError(3335_error, "No param name given");
return _end; return _end;
} }
auto nameEndPos = firstNonIdentifier(nameStartPos, _end); auto nameEndPos = firstNonIdentifier(nameStartPos, _end);
@ -149,7 +152,7 @@ DocStringParser::iter DocStringParser::parseDocTagParam(iter _pos, iter _end)
if (descStartPos == nlPos) if (descStartPos == nlPos)
{ {
appendError("No description given for param " + paramName); m_errorReporter->docstringParsingError(9942_error, "No description given for param " + paramName);
return _end; return _end;
} }
@ -189,8 +192,3 @@ void DocStringParser::newTag(string const& _tagName)
{ {
m_lastTag = &m_docTags.insert(make_pair(_tagName, DocTag()))->second; m_lastTag = &m_docTags.insert(make_pair(_tagName, DocTag()))->second;
} }
void DocStringParser::appendError(string const& _description)
{
m_errorReporter->docstringParsingError(9440_error, _description);
}

View File

@ -58,8 +58,6 @@ private:
/// Creates and inserts a new tag and adjusts m_lastTag. /// Creates and inserts a new tag and adjusts m_lastTag.
void newTag(std::string const& _tagName); void newTag(std::string const& _tagName);
void appendError(std::string const& _description);
/// Mapping tag name -> content. /// Mapping tag name -> content.
std::multimap<std::string, DocTag> m_docTags; std::multimap<std::string, DocTag> m_docTags;
DocTag* m_lastTag = nullptr; DocTag* m_lastTag = nullptr;

View File

@ -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); 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;
}
} }

View File

@ -38,4 +38,6 @@ inline bool validateUTF8(std::string const& _input)
return validateUTF8(_input, invalidPos); return validateUTF8(_input, invalidPos);
} }
size_t numCodepoints(std::string const& _utf8EncodedInput);
} }

View File

@ -382,15 +382,15 @@ bytes BinaryTransform::operator()(Loop const& _loop)
return result; 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); bytes result = std::visit(*this, *_branchIf.condition);
result += toBytes(Opcode::BrIf) + encodeLabelIdx(_breakIf.label.name); result += toBytes(Opcode::BrIf) + encodeLabelIdx(_branchIf.label.name);
return result; return result;
} }

View File

@ -48,8 +48,8 @@ public:
bytes operator()(wasm::GlobalAssignment const& _assignment); bytes operator()(wasm::GlobalAssignment const& _assignment);
bytes operator()(wasm::If const& _if); bytes operator()(wasm::If const& _if);
bytes operator()(wasm::Loop const& _loop); bytes operator()(wasm::Loop const& _loop);
bytes operator()(wasm::Break const& _break); bytes operator()(wasm::Branch const& _branch);
bytes operator()(wasm::BreakIf const& _break); bytes operator()(wasm::BranchIf const& _branchIf);
bytes operator()(wasm::Return const& _return); bytes operator()(wasm::Return const& _return);
bytes operator()(wasm::Block const& _block); bytes operator()(wasm::Block const& _block);
bytes operator()(wasm::FunctionDefinition const& _function); bytes operator()(wasm::FunctionDefinition const& _function);

View File

@ -121,14 +121,14 @@ string TextTransform::operator()(wasm::Loop const& _loop)
return "(loop" + move(label) + "\n" + indented(joinTransformed(_loop.statements, '\n')) + ")\n"; 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&) string TextTransform::operator()(wasm::Return const&)

View File

@ -48,9 +48,9 @@ public:
std::string operator()(wasm::GlobalAssignment const& _assignment); std::string operator()(wasm::GlobalAssignment const& _assignment);
std::string operator()(wasm::If const& _if); std::string operator()(wasm::If const& _if);
std::string operator()(wasm::Loop const& _loop); 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::Return const& _return);
std::string operator()(wasm::BreakIf const& _break);
std::string operator()(wasm::Block const& _block); std::string operator()(wasm::Block const& _block);
private: private:

View File

@ -41,13 +41,13 @@ struct GlobalAssignment;
struct Block; struct Block;
struct If; struct If;
struct Loop; struct Loop;
struct Break; struct Branch;
struct BreakIf; struct BranchIf;
struct Return; struct Return;
using Expression = std::variant< using Expression = std::variant<
Literal, StringLiteral, LocalVariable, GlobalVariable, Literal, StringLiteral, LocalVariable, GlobalVariable,
FunctionCall, BuiltinCall, LocalAssignment, GlobalAssignment, FunctionCall, BuiltinCall, LocalAssignment, GlobalAssignment,
Block, If, Loop, Break, BreakIf, Return Block, If, Loop, Branch, BranchIf, Return
>; >;
struct Literal { uint64_t value; }; struct Literal { uint64_t value; };
@ -66,9 +66,9 @@ struct If {
std::unique_ptr<std::vector<Expression>> elseStatements; std::unique_ptr<std::vector<Expression>> elseStatements;
}; };
struct Loop { std::string labelName; std::vector<Expression> statements; }; struct Loop { std::string labelName; std::vector<Expression> statements; };
struct Break { Label label; }; struct Branch { Label label; };
struct Return {}; 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 VariableDeclaration { std::string variableName; };
struct GlobalVariableDeclaration { std::string variableName; }; struct GlobalVariableDeclaration { std::string variableName; };

View File

@ -256,26 +256,26 @@ wasm::Expression WasmCodeTransform::operator()(ForLoop const& _for)
wasm::Loop loop; wasm::Loop loop;
loop.labelName = newLabel(); loop.labelName = newLabel();
loop.statements = visit(_for.pre.statements); 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>( wasm::BuiltinCall{"i64.eqz", make_vector<wasm::Expression>(
visitReturnByValue(*_for.condition) visitReturnByValue(*_for.condition)
)} )}
)}); )});
loop.statements.emplace_back(wasm::Block{continueLabel, visit(_for.body.statements)}); loop.statements.emplace_back(wasm::Block{continueLabel, visit(_for.body.statements)});
loop.statements += visit(_for.post.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))} }; return { wasm::Block{breakLabel, make_vector<wasm::Expression>(move(loop))} };
} }
wasm::Expression WasmCodeTransform::operator()(Break const&) 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&) 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&) wasm::Expression WasmCodeTransform::operator()(Leave const&)

View File

@ -11,3 +11,4 @@ compilability
errorstring errorstring
hist hist
otion otion
keypair

View File

@ -13,7 +13,7 @@ import hashlib
from os.path import join, isfile from os.path import join, isfile
def extract_test_cases(path): 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 inside = False
delimiter = '' delimiter = ''

View File

@ -6,7 +6,7 @@
## You can pass a branch name as argument to this script (which, if no argument is given, ## You can pass a branch name as argument to this script (which, if no argument is given,
## will default to "develop"). ## 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. ## ethereum/ethereum PPA, or ethereum/ethereum-dev PPA otherwise.
## ##
## The gnupg key for "builds@ethereum.org" has to be present in order to sign ## The gnupg key for "builds@ethereum.org" has to be present in order to sign

View File

@ -8,7 +8,7 @@ from os.path import join, isfile
def extract_test_cases(path): def extract_test_cases(path):
lines = open(path, 'rb').read().splitlines() lines = open(path, encoding="utf8", errors='ignore', mode='rb').read().splitlines()
inside = False inside = False
delimiter = '' delimiter = ''

View 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 { "©©©©ᄅ©©©©©" ; }
| ^^^^^^^^^^^^

View File

@ -0,0 +1,3 @@
contract Foo {
/* ©©©©ᄅ©©©©© 2017 */ constructor () public { "©©©©ᄅ©©©©©" ; }
}

View File

@ -193,7 +193,7 @@ contract ico is safeMath {
function setICOEthPrice(uint256 value) external { 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. 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. With this process avoiding the sudden rate changes.
@ -221,8 +221,8 @@ contract ico is safeMath {
/* /*
Closing the ICO. Closing the ICO.
It is only possible when the ICO period passed and only by the owner. 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. 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 fundation. Ethers which are situated in this contract will be sent to the address of the foundation.
*/ */
require( msg.sender == owner ); require( msg.sender == owner );
require( block.number > icoDelay ); require( block.number > icoDelay );

View File

@ -826,8 +826,8 @@ BOOST_AUTO_TEST_CASE(block_deduplicator)
Instruction::JUMP, Instruction::JUMP,
AssemblyItem(Tag, 3) AssemblyItem(Tag, 3)
}; };
BlockDeduplicator dedup(input); BlockDeduplicator deduplicator(input);
dedup.deduplicate(); deduplicator.deduplicate();
set<u256> pushTags; set<u256> pushTags;
for (AssemblyItem const& item: input) for (AssemblyItem const& item: input)
@ -857,8 +857,8 @@ BOOST_AUTO_TEST_CASE(block_deduplicator_assign_immutable_same)
AssemblyItem(PushTag, 1), AssemblyItem(PushTag, 1),
AssemblyItem(PushTag, 1), AssemblyItem(PushTag, 1),
} + blocks; } + blocks;
BlockDeduplicator dedup(input); BlockDeduplicator deduplicator(input);
dedup.deduplicate(); deduplicator.deduplicate();
BOOST_CHECK_EQUAL_COLLECTIONS(input.begin(), input.end(), output.begin(), output.end()); 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}, AssemblyItem{AssignImmutable, 0x1234},
Instruction::JUMP Instruction::JUMP
}; };
BlockDeduplicator dedup(input); BlockDeduplicator deduplicator(input);
BOOST_CHECK(!dedup.deduplicate()); BOOST_CHECK(!deduplicator.deduplicate());
} }
BOOST_AUTO_TEST_CASE(block_deduplicator_assign_immutable_different_hash) 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}, AssemblyItem{AssignImmutable, 0xABCD},
Instruction::JUMP Instruction::JUMP
}; };
BlockDeduplicator dedup(input); BlockDeduplicator deduplicator(input);
BOOST_CHECK(!dedup.deduplicate()); BOOST_CHECK(!deduplicator.deduplicate());
} }
BOOST_AUTO_TEST_CASE(block_deduplicator_loops) BOOST_AUTO_TEST_CASE(block_deduplicator_loops)
@ -920,8 +920,8 @@ BOOST_AUTO_TEST_CASE(block_deduplicator_loops)
AssemblyItem(PushTag, 2), AssemblyItem(PushTag, 2),
Instruction::JUMP, Instruction::JUMP,
}; };
BlockDeduplicator dedup(input); BlockDeduplicator deduplicator(input);
dedup.deduplicate(); deduplicator.deduplicate();
set<u256> pushTags; set<u256> pushTags;
for (AssemblyItem const& item: input) for (AssemblyItem const& item: input)

View File

@ -78,6 +78,81 @@ private:
BOOST_FIXTURE_TEST_SUITE(SolidityNatspecJSON, DocumentationChecker) 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) BOOST_AUTO_TEST_CASE(user_basic_test)
{ {
char const* sourceCode = R"( char const* sourceCode = R"(

View File

@ -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::Identifier); BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier);
BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); 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) 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::Identifier); BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier);
BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); 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) BOOST_AUTO_TEST_CASE(comment_before_eos)

View File

@ -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

View File

@ -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 ->

View File

@ -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() ->

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View 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);
}
}

View File

@ -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.

View File

@ -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.

View File

@ -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.

View File

@ -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.

View File

@ -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.

View File

@ -0,0 +1,7 @@
contract C {
///
///
function vote(uint id) public {
}
}
// ----