Introduce solidity-next pragma

Exclude pragma experimental error from ANTLR tests

Test for first pragma after non-pragma declaration

Resolve import pragmas

Change pragma name from next to solidity

Add Changelog entries

Address review comments
This commit is contained in:
Nikola Matic 2023-04-27 19:39:59 +02:00
parent 9804085934
commit 8a41f4ac7e
18 changed files with 163 additions and 14 deletions

View File

@ -5,12 +5,17 @@ Language Features:
Compiler Features:
* EWasm: Remove EWasm backend.
* Parser: Introduce ``pragma experimental solidity``, which will enable an experimental language mode that in particular has no stability guarantees between non-breaking releases and is not suited for production use.
Bugfixes:
* SMTChecker: Fix encoding of side-effects inside ``if`` and ``ternary conditional``statements in the BMC engine.
AST Changes:
* AST: Add the ``experimentalSolidity`` field to the ``SourceUnit`` nodes, which indicate whether the experimental parsing mode has been enabled via ``pragma experimental solidity``.
### 0.8.20 (2023-05-10)
Compiler Features:

View File

@ -167,9 +167,14 @@ public:
int64_t _id,
SourceLocation const& _location,
std::optional<std::string> _licenseString,
std::vector<ASTPointer<ASTNode>> _nodes
std::vector<ASTPointer<ASTNode>> _nodes,
bool _experimentalSolidity
):
ASTNode(_id, _location), m_licenseString(std::move(_licenseString)), m_nodes(std::move(_nodes)) {}
ASTNode(_id, _location),
m_licenseString(std::move(_licenseString)),
m_nodes(std::move(_nodes)),
m_experimentalSolidity(_experimentalSolidity)
{}
void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override;
@ -180,10 +185,12 @@ public:
/// @returns a set of referenced SourceUnits. Recursively if @a _recurse is true.
std::set<SourceUnit const*> referencedSourceUnits(bool _recurse = false, std::set<SourceUnit const*> _skipList = std::set<SourceUnit const*>()) const;
bool experimentalSolidity() const { return m_experimentalSolidity; }
private:
std::optional<std::string> m_licenseString;
std::vector<ASTPointer<ASTNode>> m_nodes;
bool m_experimentalSolidity = false;
};
/**

View File

@ -214,9 +214,12 @@ bool ASTJsonExporter::visit(SourceUnit const& _node)
{
std::vector<pair<string, Json::Value>> attributes = {
make_pair("license", _node.licenseString() ? Json::Value(*_node.licenseString()) : Json::nullValue),
make_pair("nodes", toJson(_node.nodes()))
make_pair("nodes", toJson(_node.nodes())),
};
if (_node.experimentalSolidity())
attributes.emplace_back("experimentalSolidity", Json::Value(_node.experimentalSolidity()));
if (_node.annotation().exportedSymbols.set())
{
Json::Value exportedSymbols = Json::objectValue;

View File

@ -271,11 +271,15 @@ ASTPointer<SourceUnit> ASTJsonImporter::createSourceUnit(Json::Value const& _nod
if (_node.isMember("license") && !_node["license"].isNull())
license = _node["license"].asString();
bool experimentalSolidity = false;
if (_node.isMember("experimentalSolidity") && !_node["experimentalSolidity"].isNull())
experimentalSolidity = _node["experimentalSolidity"].asBool();
vector<ASTPointer<ASTNode>> nodes;
for (auto& child: member(_node, "nodes"))
nodes.emplace_back(convertJsonToASTNode(child));
ASTPointer<SourceUnit> tmp = createASTNode<SourceUnit>(_node, license, nodes);
ASTPointer<SourceUnit> tmp = createASTNode<SourceUnit>(_node, license, nodes, experimentalSolidity);
tmp->annotation().path = _srcName;
return tmp;
}

View File

@ -32,7 +32,8 @@ enum class ExperimentalFeature
ABIEncoderV2, // new ABI encoder that makes use of Yul
SMTChecker,
Test,
TestOnlyAnalysis
TestOnlyAnalysis,
Solidity
};
static std::set<ExperimentalFeature> const ExperimentalFeatureWithoutWarning =
@ -48,6 +49,7 @@ static std::map<std::string, ExperimentalFeature> const ExperimentalFeatureNames
{ "SMTChecker", ExperimentalFeature::SMTChecker },
{ "__test", ExperimentalFeature::Test },
{ "__testOnlyAnalysis", ExperimentalFeature::TestOnlyAnalysis },
{ "solidity", ExperimentalFeature::Solidity }
};
}

View File

@ -429,7 +429,9 @@ bool CompilerStack::analyze()
{
if (m_stackState != ParsedAndImported || m_stackState >= AnalysisPerformed)
solThrow(CompilerError, "Must call analyze only after parsing was performed.");
resolveImports();
if (!resolveImports())
return false;
for (Source const* source: m_sourceOrder)
if (source->ast)
@ -1175,7 +1177,7 @@ string CompilerStack::applyRemapping(string const& _path, string const& _context
return m_importRemapper.apply(_path, _context);
}
void CompilerStack::resolveImports()
bool CompilerStack::resolveImports()
{
solAssert(m_stackState == ParsedAndImported, "");
@ -1200,11 +1202,34 @@ void CompilerStack::resolveImports()
sourceOrder.push_back(_source);
};
vector<PragmaDirective const*> experimentalPragmaDirectives;
for (auto const& sourcePair: m_sources)
{
if (isRequestedSource(sourcePair.first))
toposort(&sourcePair.second);
if (sourcePair.second.ast && sourcePair.second.ast->experimentalSolidity())
for (ASTPointer<ASTNode> const& node: sourcePair.second.ast->nodes())
if (PragmaDirective const* pragma = dynamic_cast<PragmaDirective*>(node.get()))
if (pragma->literals().size() >=2 && pragma->literals()[0] == "experimental" && pragma->literals()[1] == "solidity")
{
experimentalPragmaDirectives.push_back(pragma);
break;
}
}
if (!experimentalPragmaDirectives.empty() && experimentalPragmaDirectives.size() != m_sources.size())
{
for (auto &&pragma: experimentalPragmaDirectives)
m_errorReporter.parserError(
2141_error,
pragma->location(),
"File declares \"pragma experimental solidity\". If you want to enable the experimental mode, all source units must include the pragma."
);
return false;
}
swap(m_sourceOrder, sourceOrder);
return true;
}
void CompilerStack::storeContractDefinitions()

View File

@ -399,7 +399,7 @@ private:
/// @returns the newly loaded sources.
StringMap loadMissingSources(SourceUnit const& _ast);
std::string applyRemapping(std::string const& _path, std::string const& _context);
void resolveImports();
bool resolveImports();
/// Store the contract definitions in m_contracts.
void storeContractDefinitions();

View File

@ -94,14 +94,18 @@ ASTPointer<SourceUnit> Parser::parse(CharStream& _charStream)
m_recursionDepth = 0;
m_scanner = make_shared<Scanner>(_charStream);
ASTNodeFactory nodeFactory(*this);
m_experimentalSolidityEnabledInCurrentSourceUnit = false;
vector<ASTPointer<ASTNode>> nodes;
while (m_scanner->currentToken() == Token::Pragma)
nodes.push_back(parsePragmaDirective(false));
while (m_scanner->currentToken() != Token::EOS)
{
switch (m_scanner->currentToken())
{
case Token::Pragma:
nodes.push_back(parsePragmaDirective());
nodes.push_back(parsePragmaDirective(true));
break;
case Token::Import:
nodes.push_back(parseImportDirective());
@ -150,7 +154,7 @@ ASTPointer<SourceUnit> Parser::parse(CharStream& _charStream)
}
}
solAssert(m_recursionDepth == 0, "");
return nodeFactory.createNode<SourceUnit>(findLicenseString(nodes), nodes);
return nodeFactory.createNode<SourceUnit>(findLicenseString(nodes), nodes, m_experimentalSolidityEnabledInCurrentSourceUnit);
}
catch (FatalError const&)
{
@ -203,7 +207,7 @@ ASTPointer<StructuredDocumentation> Parser::parseStructuredDocumentation()
return nullptr;
}
ASTPointer<PragmaDirective> Parser::parsePragmaDirective()
ASTPointer<PragmaDirective> Parser::parsePragmaDirective(bool const _finishedParsingTopLevelPragmas)
{
RecursionGuard recursionGuard(*this);
// pragma anything* ;
@ -213,6 +217,7 @@ ASTPointer<PragmaDirective> Parser::parsePragmaDirective()
expectToken(Token::Pragma);
vector<string> literals;
vector<Token> tokens;
do
{
Token token = m_scanner->currentToken();
@ -241,6 +246,13 @@ ASTPointer<PragmaDirective> Parser::parsePragmaDirective()
);
}
if (literals.size() >= 2 && literals[0] == "experimental" && literals[1] == "solidity")
{
if (_finishedParsingTopLevelPragmas)
fatalParserError(8185_error, "Experimental pragma \"solidity\" can only be set at the beginning of the source unit.");
m_experimentalSolidityEnabledInCurrentSourceUnit = true;
}
return nodeFactory.createNode<PragmaDirective>(tokens, literals);
}

View File

@ -89,7 +89,7 @@ private:
///@name Parsing functions for the AST nodes
void parsePragmaVersion(langutil::SourceLocation const& _location, std::vector<Token> const& _tokens, std::vector<std::string> const& _literals);
ASTPointer<StructuredDocumentation> parseStructuredDocumentation();
ASTPointer<PragmaDirective> parsePragmaDirective();
ASTPointer<PragmaDirective> parsePragmaDirective(bool _finishedParsingTopLevelPragmas);
ASTPointer<ImportDirective> parseImportDirective();
/// @returns an std::pair<ContractKind, bool>, where
/// result.second is set to true, if an abstract contract was parsed, false otherwise.
@ -227,6 +227,8 @@ private:
langutil::EVMVersion m_evmVersion;
/// Counter for the next AST node ID
int64_t m_currentNodeID = 0;
/// Flag that indicates whether experimental mode is enabled in the current source unit
bool m_experimentalSolidityEnabledInCurrentSourceUnit = false;
};
}

View File

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

View File

@ -0,0 +1,21 @@
{
"absolutePath": "a",
"experimentalSolidity": true,
"exportedSymbols": {},
"id": 2,
"nodeType": "SourceUnit",
"nodes":
[
{
"id": 1,
"literals":
[
"experimental",
"solidity"
],
"nodeType": "PragmaDirective",
"src": "0:29:1"
}
],
"src": "0:30:1"
}

View File

@ -0,0 +1,3 @@
pragma experimental solidity;
// ----

View File

@ -0,0 +1,20 @@
{
"absolutePath": "a",
"experimentalSolidity": true,
"id": 2,
"nodeType": "SourceUnit",
"nodes":
[
{
"id": 1,
"literals":
[
"experimental",
"solidity"
],
"nodeType": "PragmaDirective",
"src": "0:29:1"
}
],
"src": "0:30:1"
}

View File

@ -213,7 +213,7 @@ BOOST_AUTO_TEST_CASE(type_identifiers)
ModifierDefinition mod(++id, SourceLocation{}, make_shared<string>("modif"), SourceLocation{}, {}, emptyParams, {}, {}, {});
BOOST_CHECK_EQUAL(ModifierType(mod).identifier(), "t_modifier$__$");
SourceUnit su(++id, {}, {}, {});
SourceUnit su(++id, {}, {}, {}, {});
BOOST_CHECK_EQUAL(ModuleType(su).identifier(), "t_module_7");
BOOST_CHECK_EQUAL(MagicType(MagicType::Kind::Block).identifier(), "t_magic_block");
BOOST_CHECK_EQUAL(MagicType(MagicType::Kind::Message).identifier(), "t_magic_message");

View File

@ -0,0 +1,3 @@
pragma experimental solidity;
// ----
// Warning 2264: (0-29): Experimental features are turned on. Do not use experimental features on live deployments.

View File

@ -0,0 +1,24 @@
==== Source: A.sol ====
contract A {}
==== Source: B.sol ====
pragma experimental solidity;
import "A.sol";
contract B {
A a;
}
==== Source: C.sol ====
pragma experimental solidity;
import "A.sol";
contract C {
A a;
}
==== Source: D.sol ====
pragma experimental solidity;
import "A.sol";
contract D {
A a;
}
// ----
// ParserError 2141: (B.sol:0-29): File declares "pragma experimental solidity". If you want to enable the experimental mode, all source units must include the pragma.
// ParserError 2141: (C.sol:0-29): File declares "pragma experimental solidity". If you want to enable the experimental mode, all source units must include the pragma.
// ParserError 2141: (D.sol:0-29): File declares "pragma experimental solidity". If you want to enable the experimental mode, all source units must include the pragma.

View File

@ -0,0 +1,5 @@
contract A {}
pragma experimental solidity;
// ----
// ParserError 8185: (45-45): Experimental pragma "solidity" can only be set at the beginning of the source unit.

View File

@ -0,0 +1,13 @@
function f() pure returns (uint)
{
return 1;
}
pragma experimental solidity;
struct A
{
uint256 x;
}
// ----
// ParserError 8185: (83-89): Experimental pragma "solidity" can only be set at the beginning of the source unit.