Experimental standard library

Change import syntax and cover with tests
This commit is contained in:
Nikola Matic 2023-05-18 15:22:09 +02:00
parent f1d2eda795
commit 47969adf91
17 changed files with 157 additions and 22 deletions

View File

@ -140,6 +140,7 @@ add_subdirectory(libevmasm)
add_subdirectory(libyul) add_subdirectory(libyul)
add_subdirectory(libsolidity) add_subdirectory(libsolidity)
add_subdirectory(libsolc) add_subdirectory(libsolc)
add_subdirectory(libstdlib)
add_subdirectory(tools) add_subdirectory(tools)
if (NOT EMSCRIPTEN) if (NOT EMSCRIPTEN)

View File

@ -59,6 +59,8 @@
#include <libsolidity/codegen/ir/Common.h> #include <libsolidity/codegen/ir/Common.h>
#include <libsolidity/codegen/ir/IRGenerator.h> #include <libsolidity/codegen/ir/IRGenerator.h>
#include <libstdlib/stdlib.h>
#include <libyul/YulString.h> #include <libyul/YulString.h>
#include <libyul/AsmPrinter.h> #include <libyul/AsmPrinter.h>
#include <libyul/AsmJsonConverter.h> #include <libyul/AsmJsonConverter.h>
@ -95,6 +97,7 @@ using namespace std;
using namespace solidity; using namespace solidity;
using namespace solidity::langutil; using namespace solidity::langutil;
using namespace solidity::frontend; using namespace solidity::frontend;
using namespace solidity::stdlib;
using solidity::util::errinfo_comment; using solidity::util::errinfo_comment;
@ -369,6 +372,15 @@ bool CompilerStack::parse()
for (auto const& import: ASTNode::filteredNodes<ImportDirective>(source.ast->nodes())) for (auto const& import: ASTNode::filteredNodes<ImportDirective>(source.ast->nodes()))
{ {
solAssert(!import->path().empty(), "Import path cannot be empty."); solAssert(!import->path().empty(), "Import path cannot be empty.");
// Check whether the import directive is for the standard library,
// and if yes, add specified file to source units to be parsed.
auto it = stdlib::sources.find(import->path());
if (it != stdlib::sources.end())
{
auto [name, content] = *it;
m_sources[name].charStream = make_unique<CharStream>(content, name);
sourcesToParse.push_back(name);
}
// The current value of `path` is the absolute path as seen from this source file. // The current value of `path` is the absolute path as seen from this source file.
// We first have to apply remappings before we can store the actual absolute path // We first have to apply remappings before we can store the actual absolute path

View File

@ -271,9 +271,9 @@ ASTPointer<ImportDirective> Parser::parseImportDirective()
SourceLocation unitAliasLocation{}; SourceLocation unitAliasLocation{};
ImportDirective::SymbolAliasList symbolAliases; ImportDirective::SymbolAliasList symbolAliases;
if (m_scanner->currentToken() == Token::StringLiteral) if (isQuotedPath() || isStdlibPath())
{ {
path = getLiteralAndAdvance(); path = isQuotedPath() ? getLiteralAndAdvance() : getStdlibImportPathAndAdvance();
if (m_scanner->currentToken() == Token::As) if (m_scanner->currentToken() == Token::As)
{ {
advance(); advance();
@ -315,9 +315,9 @@ ASTPointer<ImportDirective> Parser::parseImportDirective()
if (m_scanner->currentToken() != Token::Identifier || m_scanner->currentLiteral() != "from") if (m_scanner->currentToken() != Token::Identifier || m_scanner->currentLiteral() != "from")
fatalParserError(8208_error, "Expected \"from\"."); fatalParserError(8208_error, "Expected \"from\".");
advance(); advance();
if (m_scanner->currentToken() != Token::StringLiteral) if (!isQuotedPath() && !isStdlibPath())
fatalParserError(6845_error, "Expected import path."); fatalParserError(6845_error, "Expected import path.");
path = getLiteralAndAdvance(); path = isQuotedPath() ? getLiteralAndAdvance() : getStdlibImportPathAndAdvance();
} }
if (path->empty()) if (path->empty())
fatalParserError(6326_error, "Import path cannot be empty."); fatalParserError(6326_error, "Import path cannot be empty.");
@ -2486,4 +2486,25 @@ ASTPointer<ASTString> Parser::getLiteralAndAdvance()
return identifier; return identifier;
} }
bool Parser::isQuotedPath() const
{
return m_scanner->currentToken() == Token::StringLiteral;
}
bool Parser::isStdlibPath() const
{
return m_experimentalSolidityEnabledInCurrentSourceUnit
&& m_scanner->currentToken() == Token::Identifier
&& m_scanner->currentLiteral() == "std";
}
ASTPointer<ASTString> Parser::getStdlibImportPathAndAdvance()
{
ASTPointer<ASTString> std = expectIdentifierToken();
if (m_scanner->currentToken() == Token::Period)
advance();
ASTPointer<ASTString> library = expectIdentifierToken();
return make_shared<ASTString>(*std + "." + *library);
}
} }

View File

@ -219,6 +219,11 @@ private:
ASTPointer<ASTString> getLiteralAndAdvance(); ASTPointer<ASTString> getLiteralAndAdvance();
///@} ///@}
bool isQuotedPath() const;
bool isStdlibPath() const;
ASTPointer<ASTString> getStdlibImportPathAndAdvance();
/// Creates an empty ParameterList at the current location (used if parameters can be omitted). /// Creates an empty ParameterList at the current location (used if parameters can be omitted).
ASTPointer<ParameterList> createEmptyParameterList(); ASTPointer<ParameterList> createEmptyParameterList();

18
libstdlib/CMakeLists.txt Normal file
View File

@ -0,0 +1,18 @@
# This will re-generate the headers if any file within src was modified.
set_directory_properties(PROPERTY CMAKE_CONFIGURE_DEPENDS ${CMAKE_SOURCE_DIR}/stdlib/src/)
set(STDLIB stub)
set(GENERATED_STDLIB_HEADERS)
foreach(src IN LISTS STDLIB)
set(STDLIB_FILE ${CMAKE_SOURCE_DIR}/libstdlib/src/${src}.sol)
file(READ ${STDLIB_FILE} STDLIB_FILE_CONTENT HEX)
string(REGEX MATCHALL ".." STDLIB_FILE_CONTENT "${STDLIB_FILE_CONTENT}")
list(REMOVE_ITEM STDLIB_FILE_CONTENT "0d")
string(REGEX REPLACE ";" ",\n\t0x" STDLIB_FILE_CONTENT "${STDLIB_FILE_CONTENT}")
set(STDLIB_FILE_CONTENT "0x${STDLIB_FILE_CONTENT}")
set(STDLIB_FILE_NAME ${src})
configure_file("${CMAKE_SOURCE_DIR}/libstdlib/stdlib.src.h.in" ${CMAKE_BINARY_DIR}/include/libstdlib/${src}.h NEWLINE_STYLE LF @ONLY)
list(APPEND GENERATED_STDLIB_HEADERS ${CMAKE_BINARY_DIR}/include/libstdlib/${src}.h)
endforeach()
configure_file("${CMAKE_SOURCE_DIR}/libstdlib/stdlib.h.in" ${CMAKE_BINARY_DIR}/include/libstdlib/stdlib.h NEWLINE_STYLE LF @ONLY)

9
libstdlib/src/stub.sol Normal file
View File

@ -0,0 +1,9 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity >=0.0;
pragma experimental solidity;
function identity(uint256 x) pure returns (uint256)
{
return x;
}

15
libstdlib/stdlib.h.in Normal file
View File

@ -0,0 +1,15 @@
#pragma once
#include <map>
#include <string>
#include "libstdlib/stub.h"
namespace solidity::stdlib
{
static std::map<std::string, std::string> sources = {
{ "std.stub", stub }
};
} // namespace solidity::stdlib

13
libstdlib/stdlib.src.h.in Normal file
View File

@ -0,0 +1,13 @@
// The generation of this file is defined in stdlib/CMakeLists.txt.
// This file was generated by using the content of stdlib/src/@STDLIB_FILE_NAME@.sol.
#pragma once
namespace solidity::stdlib
{
static char const @STDLIB_FILE_NAME@[] = {
@STDLIB_FILE_CONTENT@, 0
};
} // namespace solidity::stdlib

View File

@ -209,7 +209,7 @@ esac
# boost_filesystem_bug specifically tests a local fix for a boost::filesystem # boost_filesystem_bug specifically tests a local fix for a boost::filesystem
# bug. Since the test involves a malformed path, there is no point in running # bug. Since the test involves a malformed path, there is no point in running
# tests on it. See https://github.com/boostorg/filesystem/issues/176 # tests on it. See https://github.com/boostorg/filesystem/issues/176
IMPORT_TEST_FILES=$(find "${TEST_DIRS[@]}" -name "*.sol" -and -not -name "boost_filesystem_bug.sol") IMPORT_TEST_FILES=$(find "${TEST_DIRS[@]}" -name "*.sol" -and -not -name "boost_filesystem_bug.sol" -not -path "*/experimental/*")
NSOURCES="$(echo "${IMPORT_TEST_FILES}" | wc -l)" NSOURCES="$(echo "${IMPORT_TEST_FILES}" | wc -l)"
echo "Looking at ${NSOURCES} .sol files..." echo "Looking at ${NSOURCES} .sol files..."

View File

@ -110,7 +110,7 @@ do
SOL_FILES+=("$line") SOL_FILES+=("$line")
done < <( done < <(
grep --include "*.sol" -riL -E \ grep --include "*.sol" -riL -E \
"^\/\/ (Syntax|Type|Declaration)Error|^\/\/ ParserError (1684|2837|3716|3997|5333|6275|6281|6933|7319|8185|7637)|^==== Source:" \ "^\/\/ (Syntax|Type|Declaration)Error|^\/\/ ParserError (1684|2837|3716|3997|5333|6275|6281|6933|7319|8185|7637)|^==== Source:|^pragma experimental solidity;" \
"${ROOT_DIR}/test/libsolidity/syntaxTests" \ "${ROOT_DIR}/test/libsolidity/syntaxTests" \
"${ROOT_DIR}/test/libsolidity/semanticTests" | "${ROOT_DIR}/test/libsolidity/semanticTests" |
# Skipping the unicode tests as I couldn't adapt the lexical grammar to recursively counting RLO/LRO/PDF's. # Skipping the unicode tests as I couldn't adapt the lexical grammar to recursively counting RLO/LRO/PDF's.

View File

@ -123,25 +123,28 @@ void SyntaxTest::filterObtainedErrors()
string sourceName; string sourceName;
if (SourceLocation const* location = currentError->sourceLocation()) if (SourceLocation const* location = currentError->sourceLocation())
{ {
locationStart = location->start;
locationEnd = location->end;
solAssert(location->sourceName, ""); solAssert(location->sourceName, "");
sourceName = *location->sourceName; sourceName = *location->sourceName;
solAssert(m_sources.sources.count(sourceName) == 1, ""); if(m_sources.sources.count(sourceName) == 1)
int preambleSize =
static_cast<int>(compiler().charStream(sourceName).size()) -
static_cast<int>(m_sources.sources[sourceName].size());
solAssert(preambleSize >= 0, "");
// ignore the version & license pragma inserted by the testing tool when calculating locations.
if (location->start != -1)
{ {
solAssert(location->start >= preambleSize, ""); int preambleSize =
locationStart = location->start - preambleSize; static_cast<int>(compiler().charStream(sourceName).size()) -
} static_cast<int>(m_sources.sources[sourceName].size());
if (location->end != -1) solAssert(preambleSize >= 0, "");
{
solAssert(location->end >= preambleSize, ""); // ignore the version & license pragma inserted by the testing tool when calculating locations.
locationEnd = location->end - preambleSize; if (location->start != -1)
{
solAssert(location->start >= preambleSize, "");
locationStart = location->start - preambleSize;
}
if (location->end != -1)
{
solAssert(location->end >= preambleSize, "");
locationEnd = location->end - preambleSize;
}
} }
} }
m_errorList.emplace_back(SyntaxTestError{ m_errorList.emplace_back(SyntaxTestError{

View File

@ -0,0 +1,8 @@
pragma experimental solidity;
import std.stub;
// ====
// EVMVersion: >=constantinople
// ----
// Warning 2264: (std.stub:63-92): Experimental features are turned on. Do not use experimental features on live deployments.
// Warning 2264: (0-29): Experimental features are turned on. Do not use experimental features on live deployments.

View File

@ -0,0 +1,8 @@
pragma experimental solidity;
import std.stub as stub;
// ====
// EVMVersion: >=constantinople
// ----
// Warning 2264: (std.stub:63-92): Experimental features are turned on. Do not use experimental features on live deployments.
// Warning 2264: (0-29): Experimental features are turned on. Do not use experimental features on live deployments.

View File

@ -0,0 +1,8 @@
pragma experimental solidity;
import { identity } from std.stub;
// ====
// EVMVersion: >=constantinople
// ----
// Warning 2264: (std.stub:63-92): Experimental features are turned on. Do not use experimental features on live deployments.
// Warning 2264: (0-29): Experimental features are turned on. Do not use experimental features on live deployments.

View File

@ -0,0 +1,8 @@
pragma experimental solidity;
import * as stub from std.stub;
// ====
// EVMVersion: >=constantinople
// ----
// Warning 2264: (std.stub:63-92): Experimental features are turned on. Do not use experimental features on live deployments.
// Warning 2264: (0-29): Experimental features are turned on. Do not use experimental features on live deployments.

View File

@ -0,0 +1,3 @@
import std.stub;
// ----
// ParserError 9478: (7-10): Expected string literal (path), "*" or alias list.

View File

@ -0,0 +1,3 @@
import { identity } from std.stub;
// ----
// ParserError 6845: (25-28): Expected import path.