Merge pull request #9776 from ethereum/reportUnimplemented

Sol->Yul: Report source locations related to unimplemented features.
This commit is contained in:
chriseth 2020-09-15 18:46:45 +02:00 committed by GitHub
commit b08b76ffca
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 212 additions and 49 deletions

View File

@ -12,6 +12,7 @@ Compiler Features:
* Yul Optimizer: Prune unused parameters in functions. * Yul Optimizer: Prune unused parameters in functions.
* Yul Optimizer: Inline into functions further down in the call graph first. * Yul Optimizer: Inline into functions further down in the call graph first.
* Yul Optimizer: Try to simplify function names. * Yul Optimizer: Try to simplify function names.
* Yul IR Generator: Report source locations related to unimplemented features.
Bugfixes: Bugfixes:

View File

@ -164,7 +164,7 @@ string IRGenerator::generate(
string IRGenerator::generate(Block const& _block) string IRGenerator::generate(Block const& _block)
{ {
IRGeneratorForStatements generator(m_context, m_utils); IRGeneratorForStatements generator(m_context, m_utils);
_block.accept(generator); generator.generate(_block);
return generator.code(); return generator.code();
} }

View File

@ -38,6 +38,8 @@
#include <libyul/Dialect.h> #include <libyul/Dialect.h>
#include <libyul/optimiser/ASTCopier.h> #include <libyul/optimiser/ASTCopier.h>
#include <liblangutil/Exceptions.h>
#include <libsolutil/Whiskers.h> #include <libsolutil/Whiskers.h>
#include <libsolutil/StringUtils.h> #include <libsolutil/StringUtils.h>
#include <libsolutil/Keccak256.h> #include <libsolutil/Keccak256.h>
@ -152,70 +154,130 @@ string IRGeneratorForStatements::code() const
return m_code.str(); return m_code.str();
} }
void IRGeneratorForStatements::generate(Block const& _block)
{
try
{
_block.accept(*this);
}
catch (langutil::UnimplementedFeatureError const& _error)
{
if (!boost::get_error_info<langutil::errinfo_sourceLocation>(_error))
_error << langutil::errinfo_sourceLocation(m_currentLocation);
throw _error;
}
}
void IRGeneratorForStatements::initializeStateVar(VariableDeclaration const& _varDecl) void IRGeneratorForStatements::initializeStateVar(VariableDeclaration const& _varDecl)
{ {
solAssert(_varDecl.immutable() || m_context.isStateVariable(_varDecl), "Must be immutable or a state variable."); try
solAssert(!_varDecl.isConstant(), ""); {
if (!_varDecl.value()) setLocation(_varDecl);
return;
_varDecl.value()->accept(*this); solAssert(_varDecl.immutable() || m_context.isStateVariable(_varDecl), "Must be immutable or a state variable.");
writeToLValue( solAssert(!_varDecl.isConstant(), "");
_varDecl.immutable() ? if (!_varDecl.value())
IRLValue{*_varDecl.annotation().type, IRLValue::Immutable{&_varDecl}} : return;
IRLValue{*_varDecl.annotation().type, IRLValue::Storage{
util::toCompactHexWithPrefix(m_context.storageLocationOfStateVariable(_varDecl).first), _varDecl.value()->accept(*this);
m_context.storageLocationOfStateVariable(_varDecl).second writeToLValue(
}}, _varDecl.immutable() ?
*_varDecl.value() IRLValue{*_varDecl.annotation().type, IRLValue::Immutable{&_varDecl}} :
); IRLValue{*_varDecl.annotation().type, IRLValue::Storage{
util::toCompactHexWithPrefix(m_context.storageLocationOfStateVariable(_varDecl).first),
m_context.storageLocationOfStateVariable(_varDecl).second
}},
*_varDecl.value()
);
}
catch (langutil::UnimplementedFeatureError const& _error)
{
if (!boost::get_error_info<langutil::errinfo_sourceLocation>(_error))
_error << langutil::errinfo_sourceLocation(m_currentLocation);
throw _error;
}
} }
void IRGeneratorForStatements::initializeLocalVar(VariableDeclaration const& _varDecl) void IRGeneratorForStatements::initializeLocalVar(VariableDeclaration const& _varDecl)
{ {
solAssert(m_context.isLocalVariable(_varDecl), "Must be a local variable."); try
{
setLocation(_varDecl);
auto const* type = _varDecl.type(); solAssert(m_context.isLocalVariable(_varDecl), "Must be a local variable.");
if (auto const* refType = dynamic_cast<ReferenceType const*>(type))
if (refType->dataStoredIn(DataLocation::Storage) && refType->isPointer())
return;
IRVariable zero = zeroValue(*type); auto const* type = _varDecl.type();
assign(m_context.localVariable(_varDecl), zero); if (auto const* refType = dynamic_cast<ReferenceType const*>(type))
if (refType->dataStoredIn(DataLocation::Storage) && refType->isPointer())
return;
IRVariable zero = zeroValue(*type);
assign(m_context.localVariable(_varDecl), zero);
}
catch (langutil::UnimplementedFeatureError const& _error)
{
if (!boost::get_error_info<langutil::errinfo_sourceLocation>(_error))
_error << langutil::errinfo_sourceLocation(m_currentLocation);
throw _error;
}
} }
IRVariable IRGeneratorForStatements::evaluateExpression(Expression const& _expression, Type const& _targetType) IRVariable IRGeneratorForStatements::evaluateExpression(Expression const& _expression, Type const& _targetType)
{ {
_expression.accept(*this); try
IRVariable variable{m_context.newYulVariable(), _targetType}; {
define(variable, _expression); setLocation(_expression);
return variable;
_expression.accept(*this);
IRVariable variable{m_context.newYulVariable(), _targetType};
define(variable, _expression);
return variable;
}
catch (langutil::UnimplementedFeatureError const& _error)
{
if (!boost::get_error_info<langutil::errinfo_sourceLocation>(_error))
_error << langutil::errinfo_sourceLocation(m_currentLocation);
throw _error;
}
} }
string IRGeneratorForStatements::constantValueFunction(VariableDeclaration const& _constant) string IRGeneratorForStatements::constantValueFunction(VariableDeclaration const& _constant)
{ {
string functionName = IRNames::constantValueFunction(_constant); try
return m_context.functionCollector().createFunction(functionName, [&] { {
Whiskers templ(R"( setLocation(_constant);
function <functionName>() -> <ret> {
<code>
<ret> := <value>
}
)");
templ("functionName", functionName);
IRGeneratorForStatements generator(m_context, m_utils);
solAssert(_constant.value(), "");
Type const& constantType = *_constant.type();
templ("value", generator.evaluateExpression(*_constant.value(), constantType).commaSeparatedList());
templ("code", generator.code());
templ("ret", IRVariable("ret", constantType).commaSeparatedList());
return templ.render(); string functionName = IRNames::constantValueFunction(_constant);
}); return m_context.functionCollector().createFunction(functionName, [&] {
Whiskers templ(R"(
function <functionName>() -> <ret> {
<code>
<ret> := <value>
}
)");
templ("functionName", functionName);
IRGeneratorForStatements generator(m_context, m_utils);
solAssert(_constant.value(), "");
Type const& constantType = *_constant.type();
templ("value", generator.evaluateExpression(*_constant.value(), constantType).commaSeparatedList());
templ("code", generator.code());
templ("ret", IRVariable("ret", constantType).commaSeparatedList());
return templ.render();
});
}
catch (langutil::UnimplementedFeatureError const& _error)
{
if (!boost::get_error_info<langutil::errinfo_sourceLocation>(_error))
_error << langutil::errinfo_sourceLocation(m_currentLocation);
throw _error;
}
} }
void IRGeneratorForStatements::endVisit(VariableDeclarationStatement const& _varDeclStatement) void IRGeneratorForStatements::endVisit(VariableDeclarationStatement const& _varDeclStatement)
{ {
setLocation(_varDeclStatement);
if (Expression const* expression = _varDeclStatement.initialValue()) if (Expression const* expression = _varDeclStatement.initialValue())
{ {
if (_varDeclStatement.declarations().size() > 1) if (_varDeclStatement.declarations().size() > 1)
@ -249,14 +311,22 @@ bool IRGeneratorForStatements::visit(Conditional const& _conditional)
{ {
_conditional.condition().accept(*this); _conditional.condition().accept(*this);
setLocation(_conditional);
string condition = expressionAsType(_conditional.condition(), *TypeProvider::boolean()); string condition = expressionAsType(_conditional.condition(), *TypeProvider::boolean());
declare(_conditional); declare(_conditional);
m_code << "switch " << condition << "\n" "case 0 {\n"; m_code << "switch " << condition << "\n" "case 0 {\n";
_conditional.falseExpression().accept(*this); _conditional.falseExpression().accept(*this);
setLocation(_conditional);
assign(_conditional, _conditional.falseExpression()); assign(_conditional, _conditional.falseExpression());
m_code << "}\n" "default {\n"; m_code << "}\n" "default {\n";
_conditional.trueExpression().accept(*this); _conditional.trueExpression().accept(*this);
setLocation(_conditional);
assign(_conditional, _conditional.trueExpression()); assign(_conditional, _conditional.trueExpression());
m_code << "}\n"; m_code << "}\n";
@ -266,6 +336,7 @@ bool IRGeneratorForStatements::visit(Conditional const& _conditional)
bool IRGeneratorForStatements::visit(Assignment const& _assignment) bool IRGeneratorForStatements::visit(Assignment const& _assignment)
{ {
_assignment.rightHandSide().accept(*this); _assignment.rightHandSide().accept(*this);
setLocation(_assignment);
Token assignmentOperator = _assignment.assignmentOperator(); Token assignmentOperator = _assignment.assignmentOperator();
Token binaryOperator = Token binaryOperator =
@ -283,6 +354,7 @@ bool IRGeneratorForStatements::visit(Assignment const& _assignment)
IRVariable value = convert(_assignment.rightHandSide(), *rightIntermediateType); IRVariable value = convert(_assignment.rightHandSide(), *rightIntermediateType);
_assignment.leftHandSide().accept(*this); _assignment.leftHandSide().accept(*this);
solAssert(!!m_currentLValue, "LValue not retrieved."); solAssert(!!m_currentLValue, "LValue not retrieved.");
setLocation(_assignment);
if (assignmentOperator != Token::Assign) if (assignmentOperator != Token::Assign)
{ {
@ -323,6 +395,8 @@ bool IRGeneratorForStatements::visit(Assignment const& _assignment)
bool IRGeneratorForStatements::visit(TupleExpression const& _tuple) bool IRGeneratorForStatements::visit(TupleExpression const& _tuple)
{ {
setLocation(_tuple);
if (_tuple.isInlineArray()) if (_tuple.isInlineArray())
{ {
auto const& arrayType = dynamic_cast<ArrayType const&>(*_tuple.annotation().type); auto const& arrayType = dynamic_cast<ArrayType const&>(*_tuple.annotation().type);
@ -339,6 +413,7 @@ bool IRGeneratorForStatements::visit(TupleExpression const& _tuple)
{ {
Expression const& component = *_tuple.components()[i]; Expression const& component = *_tuple.components()[i];
component.accept(*this); component.accept(*this);
setLocation(_tuple);
IRVariable converted = convert(component, baseType); IRVariable converted = convert(component, baseType);
m_code << m_code <<
m_utils.writeToMemoryFunction(baseType) << m_utils.writeToMemoryFunction(baseType) <<
@ -358,6 +433,7 @@ bool IRGeneratorForStatements::visit(TupleExpression const& _tuple)
{ {
solAssert(_tuple.components().front(), ""); solAssert(_tuple.components().front(), "");
_tuple.components().front()->accept(*this); _tuple.components().front()->accept(*this);
setLocation(_tuple);
if (willBeWrittenTo) if (willBeWrittenTo)
solAssert(!!m_currentLValue, ""); solAssert(!!m_currentLValue, "");
else else
@ -370,6 +446,7 @@ bool IRGeneratorForStatements::visit(TupleExpression const& _tuple)
if (auto const& component = _tuple.components()[i]) if (auto const& component = _tuple.components()[i])
{ {
component->accept(*this); component->accept(*this);
setLocation(_tuple);
if (willBeWrittenTo) if (willBeWrittenTo)
{ {
solAssert(!!m_currentLValue, ""); solAssert(!!m_currentLValue, "");
@ -395,17 +472,20 @@ bool IRGeneratorForStatements::visit(TupleExpression const& _tuple)
bool IRGeneratorForStatements::visit(IfStatement const& _ifStatement) bool IRGeneratorForStatements::visit(IfStatement const& _ifStatement)
{ {
_ifStatement.condition().accept(*this); _ifStatement.condition().accept(*this);
setLocation(_ifStatement);
string condition = expressionAsType(_ifStatement.condition(), *TypeProvider::boolean()); string condition = expressionAsType(_ifStatement.condition(), *TypeProvider::boolean());
if (_ifStatement.falseStatement()) if (_ifStatement.falseStatement())
{ {
m_code << "switch " << condition << "\n" "case 0 {\n"; m_code << "switch " << condition << "\n" "case 0 {\n";
_ifStatement.falseStatement()->accept(*this); _ifStatement.falseStatement()->accept(*this);
setLocation(_ifStatement);
m_code << "}\n" "default {\n"; m_code << "}\n" "default {\n";
} }
else else
m_code << "if " << condition << " {\n"; m_code << "if " << condition << " {\n";
_ifStatement.trueStatement().accept(*this); _ifStatement.trueStatement().accept(*this);
setLocation(_ifStatement);
m_code << "}\n"; m_code << "}\n";
return false; return false;
@ -413,6 +493,7 @@ bool IRGeneratorForStatements::visit(IfStatement const& _ifStatement)
bool IRGeneratorForStatements::visit(ForStatement const& _forStatement) bool IRGeneratorForStatements::visit(ForStatement const& _forStatement)
{ {
setLocation(_forStatement);
generateLoop( generateLoop(
_forStatement.body(), _forStatement.body(),
_forStatement.condition(), _forStatement.condition(),
@ -425,6 +506,7 @@ bool IRGeneratorForStatements::visit(ForStatement const& _forStatement)
bool IRGeneratorForStatements::visit(WhileStatement const& _whileStatement) bool IRGeneratorForStatements::visit(WhileStatement const& _whileStatement)
{ {
setLocation(_whileStatement);
generateLoop( generateLoop(
_whileStatement.body(), _whileStatement.body(),
&_whileStatement.condition(), &_whileStatement.condition(),
@ -436,20 +518,23 @@ bool IRGeneratorForStatements::visit(WhileStatement const& _whileStatement)
return false; return false;
} }
bool IRGeneratorForStatements::visit(Continue const&) bool IRGeneratorForStatements::visit(Continue const& _continue)
{ {
setLocation(_continue);
m_code << "continue\n"; m_code << "continue\n";
return false; return false;
} }
bool IRGeneratorForStatements::visit(Break const&) bool IRGeneratorForStatements::visit(Break const& _break)
{ {
setLocation(_break);
m_code << "break\n"; m_code << "break\n";
return false; return false;
} }
void IRGeneratorForStatements::endVisit(Return const& _return) void IRGeneratorForStatements::endVisit(Return const& _return)
{ {
setLocation(_return);
if (Expression const* value = _return.expression()) if (Expression const* value = _return.expression())
{ {
solAssert(_return.annotation().functionReturnParameters, "Invalid return parameters pointer."); solAssert(_return.annotation().functionReturnParameters, "Invalid return parameters pointer.");
@ -466,6 +551,7 @@ void IRGeneratorForStatements::endVisit(Return const& _return)
void IRGeneratorForStatements::endVisit(UnaryOperation const& _unaryOperation) void IRGeneratorForStatements::endVisit(UnaryOperation const& _unaryOperation)
{ {
setLocation(_unaryOperation);
Type const& resultType = type(_unaryOperation); Type const& resultType = type(_unaryOperation);
Token const op = _unaryOperation.getOperator(); Token const op = _unaryOperation.getOperator();
@ -551,6 +637,8 @@ void IRGeneratorForStatements::endVisit(UnaryOperation const& _unaryOperation)
bool IRGeneratorForStatements::visit(BinaryOperation const& _binOp) bool IRGeneratorForStatements::visit(BinaryOperation const& _binOp)
{ {
setLocation(_binOp);
solAssert(!!_binOp.annotation().commonType, ""); solAssert(!!_binOp.annotation().commonType, "");
TypePointer commonType = _binOp.annotation().commonType; TypePointer commonType = _binOp.annotation().commonType;
langutil::Token op = _binOp.getOperator(); langutil::Token op = _binOp.getOperator();
@ -570,6 +658,7 @@ bool IRGeneratorForStatements::visit(BinaryOperation const& _binOp)
_binOp.leftExpression().accept(*this); _binOp.leftExpression().accept(*this);
_binOp.rightExpression().accept(*this); _binOp.rightExpression().accept(*this);
setLocation(_binOp);
if (TokenTraits::isCompareOp(op)) if (TokenTraits::isCompareOp(op))
{ {
@ -629,6 +718,7 @@ bool IRGeneratorForStatements::visit(BinaryOperation const& _binOp)
bool IRGeneratorForStatements::visit(FunctionCall const& _functionCall) bool IRGeneratorForStatements::visit(FunctionCall const& _functionCall)
{ {
setLocation(_functionCall);
FunctionTypePointer functionType = dynamic_cast<FunctionType const*>(&type(_functionCall.expression())); FunctionTypePointer functionType = dynamic_cast<FunctionType const*>(&type(_functionCall.expression()));
if ( if (
functionType && functionType &&
@ -643,6 +733,7 @@ bool IRGeneratorForStatements::visit(FunctionCall const& _functionCall)
void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
{ {
setLocation(_functionCall);
auto functionCallKind = *_functionCall.annotation().kind; auto functionCallKind = *_functionCall.annotation().kind;
if (functionCallKind == FunctionCallKind::TypeConversion) if (functionCallKind == FunctionCallKind::TypeConversion)
@ -1360,6 +1451,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
void IRGeneratorForStatements::endVisit(FunctionCallOptions const& _options) void IRGeneratorForStatements::endVisit(FunctionCallOptions const& _options)
{ {
setLocation(_options);
FunctionType const& previousType = dynamic_cast<FunctionType const&>(*_options.expression().annotation().type); FunctionType const& previousType = dynamic_cast<FunctionType const&>(*_options.expression().annotation().type);
solUnimplementedAssert(!previousType.bound(), ""); solUnimplementedAssert(!previousType.bound(), "");
@ -1379,6 +1471,7 @@ void IRGeneratorForStatements::endVisit(FunctionCallOptions const& _options)
void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess) void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess)
{ {
setLocation(_memberAccess);
ASTString const& member = _memberAccess.memberName(); ASTString const& member = _memberAccess.memberName();
auto memberFunctionType = dynamic_cast<FunctionType const*>(_memberAccess.annotation().type); auto memberFunctionType = dynamic_cast<FunctionType const*>(_memberAccess.annotation().type);
Type::Category objectCategory = _memberAccess.expression().annotation().type->category(); Type::Category objectCategory = _memberAccess.expression().annotation().type->category();
@ -1764,6 +1857,7 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess)
bool IRGeneratorForStatements::visit(InlineAssembly const& _inlineAsm) bool IRGeneratorForStatements::visit(InlineAssembly const& _inlineAsm)
{ {
setLocation(_inlineAsm);
CopyTranslate bodyCopier{_inlineAsm.dialect(), m_context, _inlineAsm.annotation().externalReferences}; CopyTranslate bodyCopier{_inlineAsm.dialect(), m_context, _inlineAsm.annotation().externalReferences};
yul::Statement modified = bodyCopier(_inlineAsm.operations()); yul::Statement modified = bodyCopier(_inlineAsm.operations());
@ -1778,6 +1872,7 @@ bool IRGeneratorForStatements::visit(InlineAssembly const& _inlineAsm)
void IRGeneratorForStatements::endVisit(IndexAccess const& _indexAccess) void IRGeneratorForStatements::endVisit(IndexAccess const& _indexAccess)
{ {
setLocation(_indexAccess);
Type const& baseType = *_indexAccess.baseExpression().annotation().type; Type const& baseType = *_indexAccess.baseExpression().annotation().type;
if (baseType.category() == Type::Category::Mapping) if (baseType.category() == Type::Category::Mapping)
@ -1913,6 +2008,7 @@ void IRGeneratorForStatements::endVisit(IndexAccess const& _indexAccess)
void IRGeneratorForStatements::endVisit(IndexRangeAccess const& _indexRangeAccess) void IRGeneratorForStatements::endVisit(IndexRangeAccess const& _indexRangeAccess)
{ {
setLocation(_indexRangeAccess);
Type const& baseType = *_indexRangeAccess.baseExpression().annotation().type; Type const& baseType = *_indexRangeAccess.baseExpression().annotation().type;
solAssert( solAssert(
baseType.category() == Type::Category::Array || baseType.category() == Type::Category::ArraySlice, baseType.category() == Type::Category::Array || baseType.category() == Type::Category::ArraySlice,
@ -1959,6 +2055,7 @@ void IRGeneratorForStatements::endVisit(IndexRangeAccess const& _indexRangeAcces
void IRGeneratorForStatements::endVisit(Identifier const& _identifier) void IRGeneratorForStatements::endVisit(Identifier const& _identifier)
{ {
setLocation(_identifier);
Declaration const* declaration = _identifier.annotation().referencedDeclaration; Declaration const* declaration = _identifier.annotation().referencedDeclaration;
if (MagicVariableDeclaration const* magicVar = dynamic_cast<MagicVariableDeclaration const*>(declaration)) if (MagicVariableDeclaration const* magicVar = dynamic_cast<MagicVariableDeclaration const*>(declaration))
{ {
@ -2017,6 +2114,7 @@ void IRGeneratorForStatements::endVisit(Identifier const& _identifier)
bool IRGeneratorForStatements::visit(Literal const& _literal) bool IRGeneratorForStatements::visit(Literal const& _literal)
{ {
setLocation(_literal);
Type const& literalType = type(_literal); Type const& literalType = type(_literal);
switch (literalType.category()) switch (literalType.category())
@ -2039,6 +2137,7 @@ void IRGeneratorForStatements::handleVariableReference(
Expression const& _referencingExpression Expression const& _referencingExpression
) )
{ {
setLocation(_referencingExpression);
if (_variable.isStateVariable() && _variable.isConstant()) if (_variable.isStateVariable() && _variable.isConstant())
define(_referencingExpression) << constantValueFunction(_variable) << "()\n"; define(_referencingExpression) << constantValueFunction(_variable) << "()\n";
else if (_variable.isStateVariable() && _variable.immutable()) else if (_variable.isStateVariable() && _variable.immutable())
@ -2480,6 +2579,7 @@ void IRGeneratorForStatements::appendAndOrOperatorCode(BinaryOperation const& _b
solAssert(op == Token::Or || op == Token::And, ""); solAssert(op == Token::Or || op == Token::And, "");
_binOp.leftExpression().accept(*this); _binOp.leftExpression().accept(*this);
setLocation(_binOp);
IRVariable value(_binOp); IRVariable value(_binOp);
define(value, _binOp.leftExpression()); define(value, _binOp.leftExpression());
@ -2488,6 +2588,7 @@ void IRGeneratorForStatements::appendAndOrOperatorCode(BinaryOperation const& _b
else else
m_code << "if " << value.name() << " {\n"; m_code << "if " << value.name() << " {\n";
_binOp.rightExpression().accept(*this); _binOp.rightExpression().accept(*this);
setLocation(_binOp);
assign(value, _binOp.rightExpression()); assign(value, _binOp.rightExpression());
m_code << "}\n"; m_code << "}\n";
} }
@ -2687,6 +2788,7 @@ bool IRGeneratorForStatements::visit(TryStatement const& _tryStatement)
{ {
Expression const& externalCall = _tryStatement.externalCall(); Expression const& externalCall = _tryStatement.externalCall();
externalCall.accept(*this); externalCall.accept(*this);
setLocation(_tryStatement);
m_code << "switch iszero(" << IRNames::trySuccessConditionVariable(externalCall) << ")\n"; m_code << "switch iszero(" << IRNames::trySuccessConditionVariable(externalCall) << ")\n";
@ -2707,6 +2809,7 @@ bool IRGeneratorForStatements::visit(TryStatement const& _tryStatement)
} }
successClause.block().accept(*this); successClause.block().accept(*this);
setLocation(_tryStatement);
m_code << "}\n"; m_code << "}\n";
m_code << "default { // failure case\n"; m_code << "default { // failure case\n";
@ -2797,3 +2900,8 @@ bool IRGeneratorForStatements::visit(TryCatchClause const& _clause)
_clause.block().accept(*this); _clause.block().accept(*this);
return false; return false;
} }
void IRGeneratorForStatements::setLocation(ASTNode const& _node)
{
m_currentLocation = _node.location();
}

View File

@ -47,6 +47,9 @@ public:
std::string code() const; std::string code() const;
/// Generate the code for the statements in the block;
void generate(Block const& _block);
/// Generates code to initialize the given state variable. /// Generates code to initialize the given state variable.
void initializeStateVar(VariableDeclaration const& _varDecl); void initializeStateVar(VariableDeclaration const& _varDecl);
/// Generates code to initialize the given local variable. /// Generates code to initialize the given local variable.
@ -179,10 +182,13 @@ private:
static Type const& type(Expression const& _expression); static Type const& type(Expression const& _expression);
void setLocation(ASTNode const& _node);
std::ostringstream m_code; std::ostringstream m_code;
IRGenerationContext& m_context; IRGenerationContext& m_context;
YulUtilFunctions& m_utils; YulUtilFunctions& m_utils;
std::optional<IRLValue> m_currentLValue; std::optional<IRLValue> m_currentLValue;
langutil::SourceLocation m_currentLocation;
}; };
} }

View File

@ -524,6 +524,10 @@ bool CompilerStack::compile()
{ {
if (m_generateEvmBytecode) if (m_generateEvmBytecode)
compileContract(*contract, otherCompilers); compileContract(*contract, otherCompilers);
if (m_generateIR || m_generateEwasm)
generateIR(*contract);
if (m_generateEwasm)
generateEwasm(*contract);
} }
catch (Error const& _error) catch (Error const& _error)
{ {
@ -532,10 +536,28 @@ bool CompilerStack::compile()
m_errorReporter.error(_error.errorId(), _error.type(), SourceLocation(), _error.what()); m_errorReporter.error(_error.errorId(), _error.type(), SourceLocation(), _error.what());
return false; return false;
} }
if (m_generateIR || m_generateEwasm) catch (UnimplementedFeatureError const& _unimplementedError)
generateIR(*contract); {
if (m_generateEwasm) if (
generateEwasm(*contract); SourceLocation const* sourceLocation =
boost::get_error_info<langutil::errinfo_sourceLocation>(_unimplementedError)
)
{
string const* comment = _unimplementedError.comment();
m_errorReporter.error(
1834_error,
Error::Type::CodeGenerationError,
*sourceLocation,
"Unimplemented feature error" +
((comment && !comment->empty()) ? ": " + *comment : string{}) +
" in " +
_unimplementedError.lineInfo()
);
return false;
}
else
throw;
}
} }
m_stackState = CompilationSuccessful; m_stackState = CompilationSuccessful;
this->link(); this->link();

View File

@ -188,6 +188,7 @@ def examine_id_coverage(top_dir, source_id_to_file_names, new_ids_only=False):
# Warning (1878): SPDX license identifier not provided in source file. .... # Warning (1878): SPDX license identifier not provided in source file. ....
# Warning (3420): Source file does not specify required compiler version! # Warning (3420): Source file does not specify required compiler version!
test_ids |= find_ids_in_cmdline_test_err(path.join(top_dir, "test", "cmdlineTests", "error_codes", "err")) test_ids |= find_ids_in_cmdline_test_err(path.join(top_dir, "test", "cmdlineTests", "error_codes", "err"))
test_ids |= find_ids_in_cmdline_test_err(path.join(top_dir, "test", "cmdlineTests", "yul_unimplemented", "err"))
# white list of ids which are not covered by tests # white list of ids which are not covered by tests
white_ids = { white_ids = {

View File

@ -124,6 +124,7 @@ function test_solc_behaviour()
sed -i.bak -e '/^Warning (3805): This is a pre-release compiler version, please do not use it in production./d' "$stderr_path" sed -i.bak -e '/^Warning (3805): This is a pre-release compiler version, please do not use it in production./d' "$stderr_path"
sed -i.bak -e 's/\(^[ ]*auxdata: \)0x[0-9a-f]*$/\1AUXDATA REMOVED/' "$stdout_path" sed -i.bak -e 's/\(^[ ]*auxdata: \)0x[0-9a-f]*$/\1AUXDATA REMOVED/' "$stdout_path"
sed -i.bak -e 's/ Consider adding "pragma .*$//' "$stderr_path" sed -i.bak -e 's/ Consider adding "pragma .*$//' "$stderr_path"
sed -i.bak -e 's/\(Unimplemented feature error: .* in \).*$/\1FILENAME REMOVED/' "$stderr_path"
sed -i.bak -e 's/"version": "[^"]*"/"version": "VERSION REMOVED"/' "$stdout_path" sed -i.bak -e 's/"version": "[^"]*"/"version": "VERSION REMOVED"/' "$stdout_path"
# Remove trailing empty lines. Needs a line break to make OSX sed happy. # Remove trailing empty lines. Needs a line break to make OSX sed happy.
sed -i.bak -e '1{/^$/d sed -i.bak -e '1{/^$/d

View File

@ -0,0 +1 @@
--ir --error-codes

View File

@ -0,0 +1,5 @@
Error (1834): Unimplemented feature error: setToZero for type t_struct$_S_$4_storage not yet implemented! in FILENAME REMOVED
--> yul_unimplemented/input.sol:9:9:
|
9 | delete str;
| ^^^^^^^^^^

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1,11 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.0;
contract test {
struct S {
uint x;
}
S str;
constructor() {
delete str;
}
}

View File

@ -54,6 +54,12 @@ bytes SolidityExecutionFramework::multiSourceCompileContract(
m_compiler.setRevertStringBehaviour(m_revertStrings); m_compiler.setRevertStringBehaviour(m_revertStrings);
if (!m_compiler.compile()) if (!m_compiler.compile())
{ {
// The testing framework expects an exception for
// "unimplemented" yul IR generation.
if (m_compileViaYul)
for (auto const& error: m_compiler.errors())
if (error->type() == langutil::Error::Type::CodeGenerationError)
BOOST_THROW_EXCEPTION(*error);
langutil::SourceReferenceFormatter formatter(std::cerr); langutil::SourceReferenceFormatter formatter(std::cerr);
for (auto const& error: m_compiler.errors()) for (auto const& error: m_compiler.errors())