Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Parser support for explicit storage locations #15463

Open
wants to merge 12 commits into
base: develop
Choose a base branch
from
1 change: 1 addition & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
### 0.8.29 (unreleased)

Language Features:
* Introduce syntax for specifying contract storage layout base.


Compiler Features:
Expand Down
2 changes: 2 additions & 0 deletions docs/grammar/SolidityLexer.g4
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ Address: 'address';
Anonymous: 'anonymous';
As: 'as';
Assembly: 'assembly' -> pushMode(AssemblyBlockMode);
At: 'at'; // not a real keyword
Bool: 'bool';
Break: 'break';
Bytes: 'bytes';
Expand Down Expand Up @@ -54,6 +55,7 @@ Indexed: 'indexed';
Interface: 'interface';
Internal: 'internal';
Is: 'is';
Layout: 'layout'; // not a real keyword
Library: 'library';
Mapping: 'mapping';
Memory: 'memory';
Expand Down
11 changes: 8 additions & 3 deletions docs/grammar/SolidityParser.g4
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,14 @@ symbolAliases: LBrace aliases+=importAliases (Comma aliases+=importAliases)* RBr
/**
* Top-level definition of a contract.
*/
contractDefinition:
contractDefinition
locals [boolean layoutSet=false, boolean inheritanceSet=false]
:
Abstract? Contract name=identifier
inheritanceSpecifierList?
(
{!$layoutSet}? Layout At expression {$layoutSet = true;}
| {!$inheritanceSet}? inheritanceSpecifierList {$inheritanceSet = true;}
)*
LBrace contractBodyElement* RBrace;
/**
* Top-level definition of an interface.
Expand Down Expand Up @@ -420,7 +425,7 @@ inlineArrayExpression: LBrack (expression ( Comma expression)* ) RBrack;
/**
* Besides regular non-keyword Identifiers, some keywords like 'from' and 'error' can also be used as identifiers.
*/
identifier: Identifier | From | Error | Revert | Global | Transient;
identifier: Identifier | From | Error | Revert | Global | Transient | Layout | At;

literal: stringLiteral | numberLiteral | booleanLiteral | hexStringLiteral | unicodeStringLiteral;

Expand Down
11 changes: 11 additions & 0 deletions liblangutil/ErrorReporter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,17 @@ void ErrorReporter::parserError(ErrorId _error, SourceLocation const& _location,
);
}

void ErrorReporter::parserError(ErrorId _error, SourceLocation const& _location, SecondarySourceLocation const& _secondaryLocation, std::string const& _description)
{
error(
_error,
Error::Type::ParserError,
_location,
_secondaryLocation,
_description
);
}

void ErrorReporter::fatalParserError(ErrorId _error, SourceLocation const& _location, std::string const& _description)
{
fatalError(
Expand Down
1 change: 1 addition & 0 deletions liblangutil/ErrorReporter.h
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ class ErrorReporter
void fatalDeclarationError(ErrorId _error, SourceLocation const& _location, std::string const& _description);

void parserError(ErrorId _error, SourceLocation const& _location, std::string const& _description);
void parserError(ErrorId _error, SourceLocation const& _location, SecondarySourceLocation const& _secondaryLocation, std::string const& _description);

void fatalParserError(ErrorId _error, SourceLocation const& _location, std::string const& _description);

Expand Down
4 changes: 4 additions & 0 deletions libsolidity/analysis/NameAndTypeResolver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,10 @@ bool NameAndTypeResolver::resolveNamesAndTypesInternal(ASTNode& _node, bool _res
if (!resolveNamesAndTypesInternal(*baseContract, true))
success = false;

if (ASTPointer<StorageLayoutSpecifier> storageLayoutSpecifier = contract->storageLayoutSpecifier())
if (!resolveNamesAndTypesInternal(*storageLayoutSpecifier, true))
success = false;

setScope(contract);

if (success)
Expand Down
3 changes: 3 additions & 0 deletions libsolidity/analysis/TypeChecker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,9 @@ bool TypeChecker::visit(ContractDefinition const& _contract)

ASTNode::listAccept(_contract.baseContracts(), *this);

if (auto layoutSpecifier = _contract.storageLayoutSpecifier())
layoutSpecifier->accept(*this);

for (auto const& n: _contract.subNodes())
n->accept(*this);

Expand Down
29 changes: 27 additions & 2 deletions libsolidity/ast/AST.h
Original file line number Diff line number Diff line change
Expand Up @@ -509,14 +509,16 @@ class ContractDefinition: public Declaration, public StructurallyDocumented, pub
std::vector<ASTPointer<InheritanceSpecifier>> _baseContracts,
std::vector<ASTPointer<ASTNode>> _subNodes,
ContractKind _contractKind = ContractKind::Contract,
bool _abstract = false
bool _abstract = false,
ASTPointer<StorageLayoutSpecifier> _storageLayoutSpecifier = ASTPointer<StorageLayoutSpecifier>()
):
Declaration(_id, _location, _name, std::move(_nameLocation)),
StructurallyDocumented(_documentation),
m_baseContracts(std::move(_baseContracts)),
m_subNodes(std::move(_subNodes)),
m_contractKind(_contractKind),
m_abstract(_abstract)
m_abstract(_abstract),
m_storageLayoutSpecifier(_storageLayoutSpecifier)
{}

void accept(ASTVisitor& _visitor) override;
Expand Down Expand Up @@ -586,6 +588,8 @@ class ContractDefinition: public Declaration, public StructurallyDocumented, pub

bool abstract() const { return m_abstract; }

ASTPointer<StorageLayoutSpecifier> storageLayoutSpecifier() const { return m_storageLayoutSpecifier; }

ContractDefinition const* superContract(ContractDefinition const& _mostDerivedContract) const;
/// @returns the next constructor in the inheritance hierarchy.
FunctionDefinition const* nextConstructor(ContractDefinition const& _mostDerivedContract) const;
Expand All @@ -597,12 +601,33 @@ class ContractDefinition: public Declaration, public StructurallyDocumented, pub
std::vector<ASTPointer<ASTNode>> m_subNodes;
ContractKind m_contractKind;
bool m_abstract{false};
ASTPointer<StorageLayoutSpecifier> m_storageLayoutSpecifier;

util::LazyInit<std::vector<std::pair<util::FixedHash<4>, FunctionTypePointer>>> m_interfaceFunctionList[2];
util::LazyInit<std::vector<EventDefinition const*>> m_interfaceEvents;
util::LazyInit<std::multimap<std::string, FunctionDefinition const*>> m_definedFunctionsByName;
};


class StorageLayoutSpecifier : public ASTNode
{
public:
StorageLayoutSpecifier(
int64_t _id,
SourceLocation const& _location,
ASTPointer<Expression> _expression
):
ASTNode(_id, _location),
m_expression(_expression)
{}
void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override;

ASTPointer<Expression const> expression() const { return m_expression; }
private:
ASTPointer<Expression> m_expression;
};

/**
* A sequence of identifiers separated by dots used outside the expression context. Inside the expression context, this is a sequence of Identifier and MemberAccess.
*/
Expand Down
1 change: 1 addition & 0 deletions libsolidity/ast/ASTForward.h
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ class Identifier;
class ElementaryTypeNameExpression;
class Literal;
class StructuredDocumentation;
class StorageLayoutSpecifier;

/// Experimental Solidity nodes
/// @{
Expand Down
12 changes: 11 additions & 1 deletion libsolidity/ast/ASTJsonExporter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,15 @@ bool ASTJsonExporter::visit(ImportDirective const& _node)
return false;
}

bool ASTJsonExporter::visit(StorageLayoutSpecifier const& _node)
{
std::vector<std::pair<std::string, Json>> attributes = {
std::make_pair("expression", toJson(*_node.expression()))
};
setJsonNode(_node, "StorageLayoutSpecifier", std::move(attributes));
return false;
}

bool ASTJsonExporter::visit(ContractDefinition const& _node)
{
std::vector<std::pair<std::string, Json>> attributes = {
Expand All @@ -293,7 +302,8 @@ bool ASTJsonExporter::visit(ContractDefinition const& _node)
std::make_pair("usedEvents", getContainerIds(_node.interfaceEvents(false))),
std::make_pair("usedErrors", getContainerIds(_node.interfaceErrors(false))),
std::make_pair("nodes", toJson(_node.subNodes())),
std::make_pair("scope", idOrNull(_node.scope()))
std::make_pair("scope", idOrNull(_node.scope())),
std::make_pair("storageLayoutSpecifier", _node.storageLayoutSpecifier() ? toJson(*_node.storageLayoutSpecifier()) : Json())
};
addIfSet(attributes, "canonicalName", _node.annotation().canonicalName);

Expand Down
1 change: 1 addition & 0 deletions libsolidity/ast/ASTJsonExporter.h
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ class ASTJsonExporter: public ASTConstVisitor
bool visit(ElementaryTypeNameExpression const& _node) override;
bool visit(Literal const& _node) override;
bool visit(StructuredDocumentation const& _node) override;
bool visit(StorageLayoutSpecifier const& _node) override;

void endVisit(EventDefinition const&) override;

Expand Down
17 changes: 16 additions & 1 deletion libsolidity/ast/ASTJsonImporter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@
#include <boost/algorithm/string/split.hpp>
#include <boost/algorithm/string.hpp>

#include <range/v3/algorithm/find_if.hpp>

namespace solidity::frontend
{

Expand All @@ -52,6 +54,7 @@ ASTPointer<T> ASTJsonImporter::nullOrCast(Json const& _json)
}



// ============ public ===========================

std::map<std::string, ASTPointer<SourceUnit>> ASTJsonImporter::jsonToSourceUnit(std::map<std::string, Json> const& _sourceList)
Expand Down Expand Up @@ -255,6 +258,8 @@ ASTPointer<ASTNode> ASTJsonImporter::convertJsonToASTNode(Json const& _json)
return createLiteral(_json);
if (nodeType == "StructuredDocumentation")
return createDocumentation(_json);
if (nodeType == "StorageLayoutSpecifier")
return createStorageLayoutSpecifier(_json);
else
astAssert(false, "Unknown type of ASTNode: " + nodeType);

Expand Down Expand Up @@ -348,7 +353,17 @@ ASTPointer<ContractDefinition> ASTJsonImporter::createContractDefinition(Json co
baseContracts,
subNodes,
contractKind(_node),
memberAsBool(_node, "abstract")
memberAsBool(_node, "abstract"),
nullOrCast<StorageLayoutSpecifier>(member(_node, "storageLayoutSpecifier"))
);
}

ASTPointer<StorageLayoutSpecifier> ASTJsonImporter::createStorageLayoutSpecifier(Json const& _node)
{
astAssert(_node.contains("expression"));
return createASTNode<StorageLayoutSpecifier>(
_node,
std::dynamic_pointer_cast<Expression>(convertJsonToASTNode(_node["expression"]))
);
}

Expand Down
3 changes: 2 additions & 1 deletion libsolidity/ast/ASTJsonImporter.h
Original file line number Diff line number Diff line change
Expand Up @@ -131,10 +131,11 @@ class ASTJsonImporter
ASTPointer<ElementaryTypeNameExpression> createElementaryTypeNameExpression(Json const& _node);
ASTPointer<ASTNode> createLiteral(Json const& _node);
ASTPointer<StructuredDocumentation> createDocumentation(Json const& _node);
ASTPointer<StorageLayoutSpecifier> createStorageLayoutSpecifier(Json const& _node);
///@}

// =============== general helper functions ===================
/// @returns the member of a given JSON object, throws if member does not exist
/// @returns the member of a given JSON object or null if member does not exist
Json member(Json const& _node, std::string const& _name);
/// @returns the appropriate TokenObject used in parsed Strings (pragma directive or operator)
Token scanSingleToken(Json const& _node);
Expand Down
4 changes: 4 additions & 0 deletions libsolidity/ast/ASTVisitor.h
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ class ASTVisitor
virtual bool visit(ElementaryTypeNameExpression& _node) { return visitNode(_node); }
virtual bool visit(Literal& _node) { return visitNode(_node); }
virtual bool visit(StructuredDocumentation& _node) { return visitNode(_node); }
virtual bool visit(StorageLayoutSpecifier& _node) { return visitNode(_node); }
/// Experimental Solidity nodes
/// @{
virtual bool visit(TypeClassDefinition& _node) { return visitNode(_node); }
Expand Down Expand Up @@ -174,6 +175,7 @@ class ASTVisitor
virtual void endVisit(ElementaryTypeNameExpression& _node) { endVisitNode(_node); }
virtual void endVisit(Literal& _node) { endVisitNode(_node); }
virtual void endVisit(StructuredDocumentation& _node) { endVisitNode(_node); }
virtual void endVisit(StorageLayoutSpecifier& _node) { endVisitNode(_node); }
/// Experimental Solidity nodes
/// @{
virtual void endVisit(TypeClassDefinition& _node) { endVisitNode(_node); }
Expand Down Expand Up @@ -261,6 +263,7 @@ class ASTConstVisitor
virtual bool visit(ElementaryTypeNameExpression const& _node) { return visitNode(_node); }
virtual bool visit(Literal const& _node) { return visitNode(_node); }
virtual bool visit(StructuredDocumentation const& _node) { return visitNode(_node); }
virtual bool visit(StorageLayoutSpecifier const& _node) { return visitNode(_node); }
/// Experimental Solidity nodes
/// @{
virtual bool visit(TypeClassDefinition const& _node) { return visitNode(_node); }
Expand Down Expand Up @@ -326,6 +329,7 @@ class ASTConstVisitor
virtual void endVisit(ElementaryTypeNameExpression const& _node) { endVisitNode(_node); }
virtual void endVisit(Literal const& _node) { endVisitNode(_node); }
virtual void endVisit(StructuredDocumentation const& _node) { endVisitNode(_node); }
virtual void endVisit(StorageLayoutSpecifier const& _node) { endVisitNode(_node); }
/// Experimental Solidity nodes
/// @{
virtual void endVisit(TypeClassDefinition const& _node) { endVisitNode(_node); }
Expand Down
20 changes: 20 additions & 0 deletions libsolidity/ast/AST_accept.h
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ void ContractDefinition::accept(ASTVisitor& _visitor)
if (m_documentation)
m_documentation->accept(_visitor);
listAccept(m_baseContracts, _visitor);
if (m_storageLayoutSpecifier)
m_storageLayoutSpecifier->accept(_visitor);
listAccept(m_subNodes, _visitor);
}
_visitor.endVisit(*this);
Expand All @@ -105,6 +107,8 @@ void ContractDefinition::accept(ASTConstVisitor& _visitor) const
if (m_documentation)
m_documentation->accept(_visitor);
listAccept(m_baseContracts, _visitor);
if (m_storageLayoutSpecifier)
m_storageLayoutSpecifier->accept(_visitor);
listAccept(m_subNodes, _visitor);
}
_visitor.endVisit(*this);
Expand Down Expand Up @@ -1160,6 +1164,22 @@ void ForAllQuantifier::accept(ASTConstVisitor& _visitor) const
}
_visitor.endVisit(*this);
}

void StorageLayoutSpecifier::accept(ASTVisitor& _visitor)
{
if (_visitor.visit(*this))
m_expression->accept(_visitor);

_visitor.endVisit(*this);
}

void StorageLayoutSpecifier::accept(ASTConstVisitor& _visitor) const
{
if (_visitor.visit(*this))
m_expression->accept(_visitor);

_visitor.endVisit(*this);
}
/// @}

}
Loading