Merge pull request #5583 from ethereum/builtins3

Analysis phase for builtin funtions.
This commit is contained in:
chriseth 2018-12-04 15:29:20 +01:00 committed by GitHub
commit 126ed2e990
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 68 additions and 16 deletions

View File

@ -299,7 +299,13 @@ bool AsmAnalyzer::operator()(FunctionCall const& _funCall)
bool success = true;
size_t parameters = 0;
size_t returns = 0;
if (!m_currentScope->lookup(_funCall.functionName.name, Scope::Visitor(
if (BuiltinFunction const* f = m_dialect.builtins->query(_funCall.functionName.name))
{
// TODO: compare types, too
parameters = f->parameters.size();
returns = f->returns.size();
}
else if (!m_currentScope->lookup(_funCall.functionName.name, Scope::Visitor(
[&](Scope::Variable const&)
{
m_errorReporter.typeError(

View File

@ -114,7 +114,9 @@ Statement Parser::parseStatement()
expectToken(Token::Colon);
assignment.variableName.location = location();
assignment.variableName.name = YulString(currentLiteral());
if (instructions().count(assignment.variableName.name.str()))
if (m_dialect.builtins->query(assignment.variableName.name))
fatalParserError("Identifier expected, got builtin symbol.");
else if (instructions().count(assignment.variableName.name.str()))
fatalParserError("Identifier expected, got instruction name.");
assignment.location.end = endPosition();
expectToken(Token::Identifier);
@ -174,7 +176,9 @@ Statement Parser::parseStatement()
if (currentToken() == Token::Assign && peekNextToken() != Token::Colon)
{
Assignment assignment = createWithLocation<Assignment>(identifier.location);
if (m_dialect.flavour != AsmFlavour::Yul && instructions().count(identifier.name.str()))
if (m_dialect.builtins->query(identifier.name))
fatalParserError("Cannot assign to builtin function \"" + identifier.name.str() + "\".");
else if (m_dialect.flavour != AsmFlavour::Yul && instructions().count(identifier.name.str()))
fatalParserError("Cannot use instruction names for identifier names.");
advance();
assignment.variableNames.emplace_back(identifier);
@ -357,8 +361,10 @@ Parser::ElementaryOperation Parser::parseElementaryOperation()
literal = YulString{"address"};
else
literal = YulString{currentLiteral()};
// first search the set of instructions.
if (m_dialect.flavour != AsmFlavour::Yul && instructions().count(literal.str()))
// first search the set of builtins, then the instructions.
if (m_dialect.builtins->query(literal))
ret = Identifier{location(), literal};
else if (m_dialect.flavour != AsmFlavour::Yul && instructions().count(literal.str()))
{
dev::solidity::Instruction const& instr = instructions().at(literal.str());
ret = Instruction{location(), instr};
@ -592,6 +598,8 @@ YulString Parser::expectAsmIdentifier()
break;
}
}
else if (m_dialect.builtins->query(name))
fatalParserError("Cannot use builtin function name \"" + name.str() + "\" as identifier name.");
else if (instructions().count(name.str()))
fatalParserError("Cannot use instruction names for identifier names.");
expectToken(Token::Identifier);

View File

@ -22,10 +22,12 @@
#include <test/Options.h>
#include <test/libsolidity/ErrorCheck.h>
#include <test/libyul/Common.h>
#include <libyul/AsmParser.h>
#include <libyul/AsmAnalysis.h>
#include <libyul/AsmAnalysisInfo.h>
#include <libyul/Dialect.h>
#include <liblangutil/Scanner.h>
#include <liblangutil/ErrorReporter.h>
@ -47,12 +49,12 @@ namespace test
namespace
{
bool parse(string const& _source, ErrorReporter& errorReporter)
bool parse(string const& _source, Dialect const& _dialect, ErrorReporter& errorReporter)
{
try
{
auto scanner = make_shared<Scanner>(CharStream(_source, ""));
auto parserResult = yul::Parser(errorReporter, yul::Dialect::yul()).parse(scanner, false);
auto parserResult = yul::Parser(errorReporter, _dialect).parse(scanner, false);
if (parserResult)
{
yul::AsmAnalysisInfo analysisInfo;
@ -61,7 +63,7 @@ bool parse(string const& _source, ErrorReporter& errorReporter)
errorReporter,
dev::test::Options::get().evmVersion(),
boost::none,
yul::Dialect::yul()
_dialect
)).analyze(*parserResult);
}
}
@ -72,11 +74,11 @@ bool parse(string const& _source, ErrorReporter& errorReporter)
return false;
}
boost::optional<Error> parseAndReturnFirstError(string const& _source, bool _allowWarnings = true)
boost::optional<Error> parseAndReturnFirstError(string const& _source, Dialect const& _dialect, bool _allowWarnings = true)
{
ErrorList errors;
ErrorReporter errorReporter(errors);
if (!parse(_source, errorReporter))
if (!parse(_source, _dialect, errorReporter))
{
BOOST_REQUIRE(!errors.empty());
BOOST_CHECK_EQUAL(errors.size(), 1);
@ -97,29 +99,31 @@ boost::optional<Error> parseAndReturnFirstError(string const& _source, bool _all
return {};
}
bool successParse(std::string const& _source, bool _allowWarnings = true)
bool successParse(std::string const& _source, Dialect const& _dialect = Dialect::yul(), bool _allowWarnings = true)
{
return !parseAndReturnFirstError(_source, _allowWarnings);
return !parseAndReturnFirstError(_source, _dialect, _allowWarnings);
}
Error expectError(std::string const& _source, bool _allowWarnings = false)
Error expectError(std::string const& _source, Dialect const& _dialect = Dialect::yul(), bool _allowWarnings = false)
{
auto error = parseAndReturnFirstError(_source, _allowWarnings);
auto error = parseAndReturnFirstError(_source, _dialect, _allowWarnings);
BOOST_REQUIRE(error);
return *error;
}
}
#define CHECK_ERROR(text, typ, substring) \
#define CHECK_ERROR_DIALECT(text, typ, substring, dialect) \
do \
{ \
Error err = expectError((text), false); \
Error err = expectError((text), dialect, false); \
BOOST_CHECK(err.type() == (Error::Type::typ)); \
BOOST_CHECK(dev::solidity::searchErrorMessage(err, (substring))); \
} while(0)
#define CHECK_ERROR(text, typ, substring) CHECK_ERROR_DIALECT(text, typ, substring, Dialect::yul())
BOOST_AUTO_TEST_SUITE(YulParser)
BOOST_AUTO_TEST_CASE(smoke_test)
@ -300,6 +304,40 @@ BOOST_AUTO_TEST_CASE(if_statement_invalid)
BOOST_CHECK(successParse("{ if 42:u256 { } }"));
}
BOOST_AUTO_TEST_CASE(builtins_parser)
{
struct SimpleBuiltins: public Builtins
{
BuiltinFunction const* query(YulString _name) const override
{
return _name == YulString{"builtin"} ? &f : nullptr;
}
BuiltinFunction f;
};
Dialect dialect(AsmFlavour::Strict, make_shared<SimpleBuiltins>());
CHECK_ERROR_DIALECT("{ let builtin := 6 }", ParserError, "Cannot use builtin function name \"builtin\" as identifier name.", dialect);
CHECK_ERROR_DIALECT("{ function builtin() {} }", ParserError, "Cannot use builtin function name \"builtin\" as identifier name.", dialect);
CHECK_ERROR_DIALECT("{ builtin := 6 }", ParserError, "Cannot assign to builtin function \"builtin\".", dialect);
}
BOOST_AUTO_TEST_CASE(builtins_analysis)
{
struct SimpleBuiltinsAnalysis: public Builtins
{
yul::BuiltinFunction const* query(YulString _name) const override
{
return _name == YulString("builtin") ? &m_builtin : nullptr;
}
BuiltinFunction m_builtin{YulString{"builtin"}, vector<Type>(2), vector<Type>(3), false};
};
Dialect dialect(AsmFlavour::Strict, make_shared<SimpleBuiltinsAnalysis>());
BOOST_CHECK(successParse("{ let a, b, c := builtin(1, 2) }", dialect));
CHECK_ERROR_DIALECT("{ let a, b, c := builtin(1) }", TypeError, "Function expects 2 arguments but got 1", dialect);
CHECK_ERROR_DIALECT("{ let a, b := builtin(1, 2) }", DeclarationError, "Variable count mismatch: 2 variables and 3 values.", dialect);
}
BOOST_AUTO_TEST_SUITE_END()
}