From 8a41f4ac7e05f8b5f9e0d2d43e4c9e70969bdc47 Mon Sep 17 00:00:00 2001 From: Nikola Matic Date: Thu, 27 Apr 2023 19:39:59 +0200 Subject: [PATCH] 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 --- Changelog.md | 5 ++++ libsolidity/ast/AST.h | 11 +++++-- libsolidity/ast/ASTJsonExporter.cpp | 5 +++- libsolidity/ast/ASTJsonImporter.cpp | 6 +++- libsolidity/ast/ExperimentalFeatures.h | 4 ++- libsolidity/interface/CompilerStack.cpp | 29 +++++++++++++++++-- libsolidity/interface/CompilerStack.h | 2 +- libsolidity/parsing/Parser.cpp | 18 ++++++++++-- libsolidity/parsing/Parser.h | 4 ++- scripts/test_antlr_grammar.sh | 2 +- .../ASTJSON/pragma_experimental_solidity.json | 21 ++++++++++++++ .../ASTJSON/pragma_experimental_solidity.sol | 3 ++ ...ragma_experimental_solidity_parseOnly.json | 20 +++++++++++++ test/libsolidity/SolidityTypes.cpp | 2 +- .../pragma/experimental_solidity.sol | 3 ++ ...al_solidity_multisource_not_all_enable.sol | 24 +++++++++++++++ .../experimental_solidity_out_of_order_1.sol | 5 ++++ .../experimental_solidity_out_of_order_2.sol | 13 +++++++++ 18 files changed, 163 insertions(+), 14 deletions(-) create mode 100644 test/libsolidity/ASTJSON/pragma_experimental_solidity.json create mode 100644 test/libsolidity/ASTJSON/pragma_experimental_solidity.sol create mode 100644 test/libsolidity/ASTJSON/pragma_experimental_solidity_parseOnly.json create mode 100644 test/libsolidity/syntaxTests/pragma/experimental_solidity.sol create mode 100644 test/libsolidity/syntaxTests/pragma/experimental_solidity_multisource_not_all_enable.sol create mode 100644 test/libsolidity/syntaxTests/pragma/experimental_solidity_out_of_order_1.sol create mode 100644 test/libsolidity/syntaxTests/pragma/experimental_solidity_out_of_order_2.sol diff --git a/Changelog.md b/Changelog.md index b88348e1b..71ccea0df 100644 --- a/Changelog.md +++ b/Changelog.md @@ -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: diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index 317e95d43..494cbeac2 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -167,9 +167,14 @@ public: int64_t _id, SourceLocation const& _location, std::optional _licenseString, - std::vector> _nodes + std::vector> _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 referencedSourceUnits(bool _recurse = false, std::set _skipList = std::set()) const; + bool experimentalSolidity() const { return m_experimentalSolidity; } private: std::optional m_licenseString; std::vector> m_nodes; + bool m_experimentalSolidity = false; }; /** diff --git a/libsolidity/ast/ASTJsonExporter.cpp b/libsolidity/ast/ASTJsonExporter.cpp index e13cead11..da7f76936 100644 --- a/libsolidity/ast/ASTJsonExporter.cpp +++ b/libsolidity/ast/ASTJsonExporter.cpp @@ -214,9 +214,12 @@ bool ASTJsonExporter::visit(SourceUnit const& _node) { std::vector> 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; diff --git a/libsolidity/ast/ASTJsonImporter.cpp b/libsolidity/ast/ASTJsonImporter.cpp index 251219ea0..dd7098aab 100644 --- a/libsolidity/ast/ASTJsonImporter.cpp +++ b/libsolidity/ast/ASTJsonImporter.cpp @@ -271,11 +271,15 @@ ASTPointer 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> nodes; for (auto& child: member(_node, "nodes")) nodes.emplace_back(convertJsonToASTNode(child)); - ASTPointer tmp = createASTNode(_node, license, nodes); + ASTPointer tmp = createASTNode(_node, license, nodes, experimentalSolidity); tmp->annotation().path = _srcName; return tmp; } diff --git a/libsolidity/ast/ExperimentalFeatures.h b/libsolidity/ast/ExperimentalFeatures.h index 772bc4e08..ae2cb14c5 100644 --- a/libsolidity/ast/ExperimentalFeatures.h +++ b/libsolidity/ast/ExperimentalFeatures.h @@ -32,7 +32,8 @@ enum class ExperimentalFeature ABIEncoderV2, // new ABI encoder that makes use of Yul SMTChecker, Test, - TestOnlyAnalysis + TestOnlyAnalysis, + Solidity }; static std::set const ExperimentalFeatureWithoutWarning = @@ -48,6 +49,7 @@ static std::map const ExperimentalFeatureNames { "SMTChecker", ExperimentalFeature::SMTChecker }, { "__test", ExperimentalFeature::Test }, { "__testOnlyAnalysis", ExperimentalFeature::TestOnlyAnalysis }, + { "solidity", ExperimentalFeature::Solidity } }; } diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index b18834a97..dae509ed5 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -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 experimentalPragmaDirectives; for (auto const& sourcePair: m_sources) + { if (isRequestedSource(sourcePair.first)) toposort(&sourcePair.second); + if (sourcePair.second.ast && sourcePair.second.ast->experimentalSolidity()) + for (ASTPointer const& node: sourcePair.second.ast->nodes()) + if (PragmaDirective const* pragma = dynamic_cast(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() diff --git a/libsolidity/interface/CompilerStack.h b/libsolidity/interface/CompilerStack.h index ae11e233b..a01eaaa0f 100644 --- a/libsolidity/interface/CompilerStack.h +++ b/libsolidity/interface/CompilerStack.h @@ -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(); diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index a73a4fbef..39b2df131 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -94,14 +94,18 @@ ASTPointer Parser::parse(CharStream& _charStream) m_recursionDepth = 0; m_scanner = make_shared(_charStream); ASTNodeFactory nodeFactory(*this); + m_experimentalSolidityEnabledInCurrentSourceUnit = false; vector> 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 Parser::parse(CharStream& _charStream) } } solAssert(m_recursionDepth == 0, ""); - return nodeFactory.createNode(findLicenseString(nodes), nodes); + return nodeFactory.createNode(findLicenseString(nodes), nodes, m_experimentalSolidityEnabledInCurrentSourceUnit); } catch (FatalError const&) { @@ -203,7 +207,7 @@ ASTPointer Parser::parseStructuredDocumentation() return nullptr; } -ASTPointer Parser::parsePragmaDirective() +ASTPointer Parser::parsePragmaDirective(bool const _finishedParsingTopLevelPragmas) { RecursionGuard recursionGuard(*this); // pragma anything* ; @@ -213,6 +217,7 @@ ASTPointer Parser::parsePragmaDirective() expectToken(Token::Pragma); vector literals; vector tokens; + do { Token token = m_scanner->currentToken(); @@ -241,6 +246,13 @@ ASTPointer 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(tokens, literals); } diff --git a/libsolidity/parsing/Parser.h b/libsolidity/parsing/Parser.h index c7d6146be..c83b307b8 100644 --- a/libsolidity/parsing/Parser.h +++ b/libsolidity/parsing/Parser.h @@ -89,7 +89,7 @@ private: ///@name Parsing functions for the AST nodes void parsePragmaVersion(langutil::SourceLocation const& _location, std::vector const& _tokens, std::vector const& _literals); ASTPointer parseStructuredDocumentation(); - ASTPointer parsePragmaDirective(); + ASTPointer parsePragmaDirective(bool _finishedParsingTopLevelPragmas); ASTPointer parseImportDirective(); /// @returns an std::pair, 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; }; } diff --git a/scripts/test_antlr_grammar.sh b/scripts/test_antlr_grammar.sh index 3394534f8..6ab28fc5f 100755 --- a/scripts/test_antlr_grammar.sh +++ b/scripts/test_antlr_grammar.sh @@ -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. diff --git a/test/libsolidity/ASTJSON/pragma_experimental_solidity.json b/test/libsolidity/ASTJSON/pragma_experimental_solidity.json new file mode 100644 index 000000000..d2f7e56ad --- /dev/null +++ b/test/libsolidity/ASTJSON/pragma_experimental_solidity.json @@ -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" +} diff --git a/test/libsolidity/ASTJSON/pragma_experimental_solidity.sol b/test/libsolidity/ASTJSON/pragma_experimental_solidity.sol new file mode 100644 index 000000000..e0c1c81aa --- /dev/null +++ b/test/libsolidity/ASTJSON/pragma_experimental_solidity.sol @@ -0,0 +1,3 @@ +pragma experimental solidity; + +// ---- diff --git a/test/libsolidity/ASTJSON/pragma_experimental_solidity_parseOnly.json b/test/libsolidity/ASTJSON/pragma_experimental_solidity_parseOnly.json new file mode 100644 index 000000000..a1a3410ff --- /dev/null +++ b/test/libsolidity/ASTJSON/pragma_experimental_solidity_parseOnly.json @@ -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" +} diff --git a/test/libsolidity/SolidityTypes.cpp b/test/libsolidity/SolidityTypes.cpp index 48a0441b4..db43f49c0 100644 --- a/test/libsolidity/SolidityTypes.cpp +++ b/test/libsolidity/SolidityTypes.cpp @@ -213,7 +213,7 @@ BOOST_AUTO_TEST_CASE(type_identifiers) ModifierDefinition mod(++id, SourceLocation{}, make_shared("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"); diff --git a/test/libsolidity/syntaxTests/pragma/experimental_solidity.sol b/test/libsolidity/syntaxTests/pragma/experimental_solidity.sol new file mode 100644 index 000000000..9bb74a7ec --- /dev/null +++ b/test/libsolidity/syntaxTests/pragma/experimental_solidity.sol @@ -0,0 +1,3 @@ +pragma experimental solidity; +// ---- +// Warning 2264: (0-29): Experimental features are turned on. Do not use experimental features on live deployments. diff --git a/test/libsolidity/syntaxTests/pragma/experimental_solidity_multisource_not_all_enable.sol b/test/libsolidity/syntaxTests/pragma/experimental_solidity_multisource_not_all_enable.sol new file mode 100644 index 000000000..747b8e166 --- /dev/null +++ b/test/libsolidity/syntaxTests/pragma/experimental_solidity_multisource_not_all_enable.sol @@ -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. diff --git a/test/libsolidity/syntaxTests/pragma/experimental_solidity_out_of_order_1.sol b/test/libsolidity/syntaxTests/pragma/experimental_solidity_out_of_order_1.sol new file mode 100644 index 000000000..5fde260f6 --- /dev/null +++ b/test/libsolidity/syntaxTests/pragma/experimental_solidity_out_of_order_1.sol @@ -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. diff --git a/test/libsolidity/syntaxTests/pragma/experimental_solidity_out_of_order_2.sol b/test/libsolidity/syntaxTests/pragma/experimental_solidity_out_of_order_2.sol new file mode 100644 index 000000000..3748a5c76 --- /dev/null +++ b/test/libsolidity/syntaxTests/pragma/experimental_solidity_out_of_order_2.sol @@ -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.