Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
11 changes: 9 additions & 2 deletions libsolidity/ast/AST.h
Original file line number Diff line number Diff line change
Expand Up @@ -167,9 +167,14 @@ class SourceUnit: public ASTNode, public ScopeOpener
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;
Expand All @@ -180,10 +185,12 @@ class SourceUnit: public ASTNode, public ScopeOpener

/// @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;
};

/**
Expand Down
5 changes: 4 additions & 1 deletion libsolidity/ast/ASTJsonExporter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
6 changes: 5 additions & 1 deletion libsolidity/ast/ASTJsonImporter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
4 changes: 3 additions & 1 deletion libsolidity/ast/ExperimentalFeatures.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 =
Expand All @@ -48,6 +49,7 @@ static std::map<std::string, ExperimentalFeature> const ExperimentalFeatureNames
{ "SMTChecker", ExperimentalFeature::SMTChecker },
{ "__test", ExperimentalFeature::Test },
{ "__testOnlyAnalysis", ExperimentalFeature::TestOnlyAnalysis },
{ "solidity", ExperimentalFeature::Solidity }
};

}
29 changes: 27 additions & 2 deletions libsolidity/interface/CompilerStack.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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, "");

Expand All @@ -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()
Expand Down
2 changes: 1 addition & 1 deletion libsolidity/interface/CompilerStack.h
Original file line number Diff line number Diff line change
Expand Up @@ -399,7 +399,7 @@ class CompilerStack: public langutil::CharStreamProvider
/// @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();
Expand Down
18 changes: 15 additions & 3 deletions libsolidity/parsing/Parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand Down Expand Up @@ -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&)
{
Expand Down Expand Up @@ -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* ;
Expand All @@ -213,6 +217,7 @@ ASTPointer<PragmaDirective> Parser::parsePragmaDirective()
expectToken(Token::Pragma);
vector<string> literals;
vector<Token> tokens;

do
{
Token token = m_scanner->currentToken();
Expand Down Expand Up @@ -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);
}

Expand Down
4 changes: 3 additions & 1 deletion libsolidity/parsing/Parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ class Parser: public langutil::ParserBase
///@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.
Expand Down Expand Up @@ -227,6 +227,8 @@ class Parser: public langutil::ParserBase
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;
};

}
2 changes: 1 addition & 1 deletion scripts/test_antlr_grammar.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
21 changes: 21 additions & 0 deletions test/libsolidity/ASTJSON/pragma_experimental_solidity.json
Original file line number Diff line number Diff line change
@@ -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"
}
3 changes: 3 additions & 0 deletions test/libsolidity/ASTJSON/pragma_experimental_solidity.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
pragma experimental solidity;

// ----
Original file line number Diff line number Diff line change
@@ -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"
}
2 changes: 1 addition & 1 deletion test/libsolidity/SolidityTypes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down
3 changes: 3 additions & 0 deletions test/libsolidity/syntaxTests/pragma/experimental_solidity.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
pragma experimental solidity;
// ----
// Warning 2264: (0-29): Experimental features are turned on. Do not use experimental features on live deployments.
Original file line number Diff line number Diff line change
@@ -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.
Original file line number Diff line number Diff line change
@@ -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.
Original file line number Diff line number Diff line change
@@ -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.