diff options
author | chriseth <chris@ethereum.org> | 2018-12-04 22:29:20 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-12-04 22:29:20 +0800 |
commit | 126ed2e990280b7200a7872d8d6f5b8db14e4b0a (patch) | |
tree | 89624148ff8018c19742a737c45ed4a7c3dff573 | |
parent | 8654f8f6d4cb8f609d5b43df217aff1406acbe6a (diff) | |
parent | 852297fa2f4c4ac0c3aa56011f70c667fafcb1fd (diff) | |
download | dexon-solidity-126ed2e990280b7200a7872d8d6f5b8db14e4b0a.tar.gz dexon-solidity-126ed2e990280b7200a7872d8d6f5b8db14e4b0a.tar.zst dexon-solidity-126ed2e990280b7200a7872d8d6f5b8db14e4b0a.zip |
Merge pull request #5583 from ethereum/builtins3
Analysis phase for builtin funtions.
-rw-r--r-- | libyul/AsmAnalysis.cpp | 8 | ||||
-rw-r--r-- | libyul/AsmParser.cpp | 16 | ||||
-rw-r--r-- | test/libyul/Parser.cpp | 60 |
3 files changed, 68 insertions, 16 deletions
diff --git a/libyul/AsmAnalysis.cpp b/libyul/AsmAnalysis.cpp index 5215e5c2..3cbed9c7 100644 --- a/libyul/AsmAnalysis.cpp +++ b/libyul/AsmAnalysis.cpp @@ -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( diff --git a/libyul/AsmParser.cpp b/libyul/AsmParser.cpp index 5f393b29..33bb42f9 100644 --- a/libyul/AsmParser.cpp +++ b/libyul/AsmParser.cpp @@ -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); diff --git a/test/libyul/Parser.cpp b/test/libyul/Parser.cpp index bbaf01bf..eb2e735a 100644 --- a/test/libyul/Parser.cpp +++ b/test/libyul/Parser.cpp @@ -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() } |