Skip to content

Commit e844a8b

Browse files
matheusaaguiarcameel
authored andcommitted
Parse contract layout specification
1 parent 53ab29f commit e844a8b

File tree

78 files changed

+684
-16
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

78 files changed

+684
-16
lines changed

Changelog.md

+1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
### 0.8.29 (unreleased)
22

33
Language Features:
4+
* Introduce syntax for specifying contract storage layout base.
45

56

67
Compiler Features:

docs/grammar/SolidityLexer.g4

+2
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ Address: 'address';
1414
Anonymous: 'anonymous';
1515
As: 'as';
1616
Assembly: 'assembly' -> pushMode(AssemblyBlockMode);
17+
At: 'at'; // not a real keyword
1718
Bool: 'bool';
1819
Break: 'break';
1920
Bytes: 'bytes';
@@ -54,6 +55,7 @@ Indexed: 'indexed';
5455
Interface: 'interface';
5556
Internal: 'internal';
5657
Is: 'is';
58+
Layout: 'layout'; // not a real keyword
5759
Library: 'library';
5860
Mapping: 'mapping';
5961
Memory: 'memory';

docs/grammar/SolidityParser.g4

+9-4
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,14 @@ symbolAliases: LBrace aliases+=importAliases (Comma aliases+=importAliases)* RBr
5252
/**
5353
* Top-level definition of a contract.
5454
*/
55-
contractDefinition:
55+
contractDefinition
56+
locals [boolean layoutSet=false, boolean inheritanceSet=false]
57+
:
5658
Abstract? Contract name=identifier
57-
inheritanceSpecifierList?
59+
(
60+
{!$layoutSet}? Layout At expression {$layoutSet = true;}
61+
| {!$inheritanceSet}? inheritanceSpecifierList {$inheritanceSet = true;}
62+
)*
5863
LBrace contractBodyElement* RBrace;
5964
/**
6065
* Top-level definition of an interface.
@@ -379,7 +384,7 @@ expression:
379384
expression LBrack index=expression? RBrack # IndexAccess
380385
| expression LBrack startIndex=expression? Colon endIndex=expression? RBrack # IndexRangeAccess
381386
| expression Period (identifier | Address) # MemberAccess
382-
| expression LBrace (namedArgument (Comma namedArgument)*)? RBrace # FunctionCallOptions
387+
| expression LBrace (namedArgument (Comma namedArgument)*) RBrace # FunctionCallOptions
383388
| expression callArgumentList # FunctionCall
384389
| Payable callArgumentList # PayableConversion
385390
| Type LParen typeName RParen # MetaType
@@ -420,7 +425,7 @@ inlineArrayExpression: LBrack (expression ( Comma expression)* ) RBrack;
420425
/**
421426
* Besides regular non-keyword Identifiers, some keywords like 'from' and 'error' can also be used as identifiers.
422427
*/
423-
identifier: Identifier | From | Error | Revert | Global | Transient;
428+
identifier: Identifier | From | Error | Revert | Global | Transient | Layout | At;
424429

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

liblangutil/ErrorReporter.cpp

+11
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,17 @@ void ErrorReporter::parserError(ErrorId _error, SourceLocation const& _location,
190190
);
191191
}
192192

193+
void ErrorReporter::parserError(ErrorId _error, SourceLocation const& _location, SecondarySourceLocation const& _secondaryLocation, std::string const& _description)
194+
{
195+
error(
196+
_error,
197+
Error::Type::ParserError,
198+
_location,
199+
_secondaryLocation,
200+
_description
201+
);
202+
}
203+
193204
void ErrorReporter::fatalParserError(ErrorId _error, SourceLocation const& _location, std::string const& _description)
194205
{
195206
fatalError(

liblangutil/ErrorReporter.h

+1
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ class ErrorReporter
8787
void fatalDeclarationError(ErrorId _error, SourceLocation const& _location, std::string const& _description);
8888

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

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

libsolidity/analysis/NameAndTypeResolver.cpp

+4
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,10 @@ bool NameAndTypeResolver::resolveNamesAndTypesInternal(ASTNode& _node, bool _res
299299
if (!resolveNamesAndTypesInternal(*baseContract, true))
300300
success = false;
301301

302+
if (StorageLayoutSpecifier* storageLayoutSpecifier = contract->storageLayoutSpecifier())
303+
if (!resolveNamesAndTypesInternal(*storageLayoutSpecifier, true))
304+
success = false;
305+
302306
setScope(contract);
303307

304308
if (success)

libsolidity/analysis/TypeChecker.cpp

+3
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,9 @@ bool TypeChecker::visit(ContractDefinition const& _contract)
8686

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

89+
if (StorageLayoutSpecifier const* layoutSpecifier = _contract.storageLayoutSpecifier())
90+
layoutSpecifier->accept(*this);
91+
8992
for (auto const& n: _contract.subNodes())
9093
n->accept(*this);
9194

libsolidity/ast/AST.cpp

+11
Original file line numberDiff line numberDiff line change
@@ -371,6 +371,17 @@ std::multimap<std::string, FunctionDefinition const*> const& ContractDefinition:
371371
});
372372
}
373373

374+
StorageLayoutSpecifier::StorageLayoutSpecifier(
375+
int64_t _id,
376+
SourceLocation const& _location,
377+
ASTPointer<Expression> _baseSlotExpression
378+
):
379+
ASTNode(_id, _location),
380+
m_baseSlotExpression(_baseSlotExpression)
381+
{
382+
solAssert(m_baseSlotExpression);
383+
solAssert(_location.contains(m_baseSlotExpression->location()));
384+
}
374385

375386
TypeNameAnnotation& TypeName::annotation() const
376387
{

libsolidity/ast/AST.h

+25-2
Original file line numberDiff line numberDiff line change
@@ -509,14 +509,16 @@ class ContractDefinition: public Declaration, public StructurallyDocumented, pub
509509
std::vector<ASTPointer<InheritanceSpecifier>> _baseContracts,
510510
std::vector<ASTPointer<ASTNode>> _subNodes,
511511
ContractKind _contractKind = ContractKind::Contract,
512-
bool _abstract = false
512+
bool _abstract = false,
513+
ASTPointer<StorageLayoutSpecifier> _storageLayoutSpecifier = nullptr
513514
):
514515
Declaration(_id, _location, _name, std::move(_nameLocation)),
515516
StructurallyDocumented(_documentation),
516517
m_baseContracts(std::move(_baseContracts)),
517518
m_subNodes(std::move(_subNodes)),
518519
m_contractKind(_contractKind),
519-
m_abstract(_abstract)
520+
m_abstract(_abstract),
521+
m_storageLayoutSpecifier(_storageLayoutSpecifier)
520522
{}
521523

522524
void accept(ASTVisitor& _visitor) override;
@@ -586,6 +588,9 @@ class ContractDefinition: public Declaration, public StructurallyDocumented, pub
586588

587589
bool abstract() const { return m_abstract; }
588590

591+
StorageLayoutSpecifier const* storageLayoutSpecifier() const { return m_storageLayoutSpecifier.get(); }
592+
StorageLayoutSpecifier* storageLayoutSpecifier() { return m_storageLayoutSpecifier.get(); }
593+
589594
ContractDefinition const* superContract(ContractDefinition const& _mostDerivedContract) const;
590595
/// @returns the next constructor in the inheritance hierarchy.
591596
FunctionDefinition const* nextConstructor(ContractDefinition const& _mostDerivedContract) const;
@@ -597,12 +602,30 @@ class ContractDefinition: public Declaration, public StructurallyDocumented, pub
597602
std::vector<ASTPointer<ASTNode>> m_subNodes;
598603
ContractKind m_contractKind;
599604
bool m_abstract{false};
605+
ASTPointer<StorageLayoutSpecifier> m_storageLayoutSpecifier;
600606

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

612+
613+
class StorageLayoutSpecifier : public ASTNode
614+
{
615+
public:
616+
StorageLayoutSpecifier(
617+
int64_t _id,
618+
SourceLocation const& _location,
619+
ASTPointer<Expression> _baseSlotExpression
620+
);
621+
void accept(ASTVisitor& _visitor) override;
622+
void accept(ASTConstVisitor& _visitor) const override;
623+
624+
Expression const& baseSlotExpression() const { solAssert(m_baseSlotExpression); return *m_baseSlotExpression; }
625+
private:
626+
ASTPointer<Expression> m_baseSlotExpression;
627+
};
628+
606629
/**
607630
* A sequence of identifiers separated by dots used outside the expression context. Inside the expression context, this is a sequence of Identifier and MemberAccess.
608631
*/

libsolidity/ast/ASTForward.h

+1
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ class Identifier;
9898
class ElementaryTypeNameExpression;
9999
class Literal;
100100
class StructuredDocumentation;
101+
class StorageLayoutSpecifier;
101102

102103
/// Experimental Solidity nodes
103104
/// @{

libsolidity/ast/ASTJsonExporter.cpp

+17-1
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,13 @@ Json ASTJsonExporter::toJson(ASTNode const& _node)
210210
return util::removeNullMembers(std::move(m_currentValue));
211211
}
212212

213+
Json ASTJsonExporter::toJson(ASTNode const* _node)
214+
{
215+
if (!_node)
216+
return Json();
217+
return toJson(*_node);
218+
}
219+
213220
bool ASTJsonExporter::visit(SourceUnit const& _node)
214221
{
215222
std::vector<std::pair<std::string, Json>> attributes = {
@@ -279,6 +286,14 @@ bool ASTJsonExporter::visit(ImportDirective const& _node)
279286
return false;
280287
}
281288

289+
bool ASTJsonExporter::visit(StorageLayoutSpecifier const& _node)
290+
{
291+
setJsonNode(_node, "StorageLayoutSpecifier", {
292+
{"baseSlotExpression", toJson(_node.baseSlotExpression())}
293+
});
294+
return false;
295+
}
296+
282297
bool ASTJsonExporter::visit(ContractDefinition const& _node)
283298
{
284299
std::vector<std::pair<std::string, Json>> attributes = {
@@ -293,7 +308,8 @@ bool ASTJsonExporter::visit(ContractDefinition const& _node)
293308
std::make_pair("usedEvents", getContainerIds(_node.interfaceEvents(false))),
294309
std::make_pair("usedErrors", getContainerIds(_node.interfaceErrors(false))),
295310
std::make_pair("nodes", toJson(_node.subNodes())),
296-
std::make_pair("scope", idOrNull(_node.scope()))
311+
std::make_pair("scope", idOrNull(_node.scope())),
312+
std::make_pair("storageLayout", toJson(_node.storageLayoutSpecifier()))
297313
};
298314
addIfSet(attributes, "canonicalName", _node.annotation().canonicalName);
299315

libsolidity/ast/ASTJsonExporter.h

+2
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ class ASTJsonExporter: public ASTConstVisitor
6060
/// Output the json representation of the AST to _stream.
6161
void print(std::ostream& _stream, ASTNode const& _node, util::JsonFormat const& _format);
6262
Json toJson(ASTNode const& _node);
63+
Json toJson(ASTNode const* _node);
6364
template <class T>
6465
Json toJson(std::vector<ASTPointer<T>> const& _nodes)
6566
{
@@ -126,6 +127,7 @@ class ASTJsonExporter: public ASTConstVisitor
126127
bool visit(ElementaryTypeNameExpression const& _node) override;
127128
bool visit(Literal const& _node) override;
128129
bool visit(StructuredDocumentation const& _node) override;
130+
bool visit(StorageLayoutSpecifier const& _node) override;
129131

130132
void endVisit(EventDefinition const&) override;
131133

libsolidity/ast/ASTJsonImporter.cpp

+15-1
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@
3737
#include <boost/algorithm/string/split.hpp>
3838
#include <boost/algorithm/string.hpp>
3939

40+
#include <range/v3/algorithm/find_if.hpp>
41+
4042
namespace solidity::frontend
4143
{
4244

@@ -255,6 +257,8 @@ ASTPointer<ASTNode> ASTJsonImporter::convertJsonToASTNode(Json const& _json)
255257
return createLiteral(_json);
256258
if (nodeType == "StructuredDocumentation")
257259
return createDocumentation(_json);
260+
if (nodeType == "StorageLayoutSpecifier")
261+
return createStorageLayoutSpecifier(_json);
258262
else
259263
astAssert(false, "Unknown type of ASTNode: " + nodeType);
260264

@@ -348,7 +352,17 @@ ASTPointer<ContractDefinition> ASTJsonImporter::createContractDefinition(Json co
348352
baseContracts,
349353
subNodes,
350354
contractKind(_node),
351-
memberAsBool(_node, "abstract")
355+
memberAsBool(_node, "abstract"),
356+
nullOrCast<StorageLayoutSpecifier>(member(_node, "storageLayout"))
357+
);
358+
}
359+
360+
ASTPointer<StorageLayoutSpecifier> ASTJsonImporter::createStorageLayoutSpecifier(Json const& _node)
361+
{
362+
astAssert(_node.contains("baseSlotExpression"), "Expected field \"baseSlotExpression\" is missing.");
363+
return createASTNode<StorageLayoutSpecifier>(
364+
_node,
365+
convertJsonToASTNode<Expression>(_node["baseSlotExpression"])
352366
);
353367
}
354368

libsolidity/ast/ASTJsonImporter.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -131,10 +131,11 @@ class ASTJsonImporter
131131
ASTPointer<ElementaryTypeNameExpression> createElementaryTypeNameExpression(Json const& _node);
132132
ASTPointer<ASTNode> createLiteral(Json const& _node);
133133
ASTPointer<StructuredDocumentation> createDocumentation(Json const& _node);
134+
ASTPointer<StorageLayoutSpecifier> createStorageLayoutSpecifier(Json const& _node);
134135
///@}
135136

136137
// =============== general helper functions ===================
137-
/// @returns the member of a given JSON object, throws if member does not exist
138+
/// @returns the member of a given JSON object or null if member does not exist
138139
Json member(Json const& _node, std::string const& _name);
139140
/// @returns the appropriate TokenObject used in parsed Strings (pragma directive or operator)
140141
Token scanSingleToken(Json const& _node);

libsolidity/ast/ASTVisitor.h

+4
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ class ASTVisitor
109109
virtual bool visit(ElementaryTypeNameExpression& _node) { return visitNode(_node); }
110110
virtual bool visit(Literal& _node) { return visitNode(_node); }
111111
virtual bool visit(StructuredDocumentation& _node) { return visitNode(_node); }
112+
virtual bool visit(StorageLayoutSpecifier& _node) { return visitNode(_node); }
112113
/// Experimental Solidity nodes
113114
/// @{
114115
virtual bool visit(TypeClassDefinition& _node) { return visitNode(_node); }
@@ -174,6 +175,7 @@ class ASTVisitor
174175
virtual void endVisit(ElementaryTypeNameExpression& _node) { endVisitNode(_node); }
175176
virtual void endVisit(Literal& _node) { endVisitNode(_node); }
176177
virtual void endVisit(StructuredDocumentation& _node) { endVisitNode(_node); }
178+
virtual void endVisit(StorageLayoutSpecifier& _node) { endVisitNode(_node); }
177179
/// Experimental Solidity nodes
178180
/// @{
179181
virtual void endVisit(TypeClassDefinition& _node) { endVisitNode(_node); }
@@ -261,6 +263,7 @@ class ASTConstVisitor
261263
virtual bool visit(ElementaryTypeNameExpression const& _node) { return visitNode(_node); }
262264
virtual bool visit(Literal const& _node) { return visitNode(_node); }
263265
virtual bool visit(StructuredDocumentation const& _node) { return visitNode(_node); }
266+
virtual bool visit(StorageLayoutSpecifier const& _node) { return visitNode(_node); }
264267
/// Experimental Solidity nodes
265268
/// @{
266269
virtual bool visit(TypeClassDefinition const& _node) { return visitNode(_node); }
@@ -326,6 +329,7 @@ class ASTConstVisitor
326329
virtual void endVisit(ElementaryTypeNameExpression const& _node) { endVisitNode(_node); }
327330
virtual void endVisit(Literal const& _node) { endVisitNode(_node); }
328331
virtual void endVisit(StructuredDocumentation const& _node) { endVisitNode(_node); }
332+
virtual void endVisit(StorageLayoutSpecifier const& _node) { endVisitNode(_node); }
329333
/// Experimental Solidity nodes
330334
/// @{
331335
virtual void endVisit(TypeClassDefinition const& _node) { endVisitNode(_node); }

libsolidity/ast/AST_accept.h

+20
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,8 @@ void ContractDefinition::accept(ASTVisitor& _visitor)
9393
if (m_documentation)
9494
m_documentation->accept(_visitor);
9595
listAccept(m_baseContracts, _visitor);
96+
if (m_storageLayoutSpecifier)
97+
m_storageLayoutSpecifier->accept(_visitor);
9698
listAccept(m_subNodes, _visitor);
9799
}
98100
_visitor.endVisit(*this);
@@ -105,6 +107,8 @@ void ContractDefinition::accept(ASTConstVisitor& _visitor) const
105107
if (m_documentation)
106108
m_documentation->accept(_visitor);
107109
listAccept(m_baseContracts, _visitor);
110+
if (m_storageLayoutSpecifier)
111+
m_storageLayoutSpecifier->accept(_visitor);
108112
listAccept(m_subNodes, _visitor);
109113
}
110114
_visitor.endVisit(*this);
@@ -1160,6 +1164,22 @@ void ForAllQuantifier::accept(ASTConstVisitor& _visitor) const
11601164
}
11611165
_visitor.endVisit(*this);
11621166
}
1167+
1168+
void StorageLayoutSpecifier::accept(ASTVisitor& _visitor)
1169+
{
1170+
if (_visitor.visit(*this))
1171+
m_baseSlotExpression->accept(_visitor);
1172+
1173+
_visitor.endVisit(*this);
1174+
}
1175+
1176+
void StorageLayoutSpecifier::accept(ASTConstVisitor& _visitor) const
1177+
{
1178+
if (_visitor.visit(*this))
1179+
m_baseSlotExpression->accept(_visitor);
1180+
1181+
_visitor.endVisit(*this);
1182+
}
11631183
/// @}
11641184

11651185
}

libsolidity/interface/CompilerStack.cpp

+3-1
Original file line numberDiff line numberDiff line change
@@ -1104,6 +1104,7 @@ Json const& CompilerStack::storageLayout(Contract const& _contract) const
11041104
solAssert(m_stackState >= AnalysisSuccessful, "Analysis was not successful.");
11051105
solAssert(_contract.contract);
11061106
solUnimplementedAssert(!isExperimentalSolidity());
1107+
solUnimplementedAssert(!_contract.contract->storageLayoutSpecifier(), "Storage layout not supported for contract with specified layout base.");
11071108

11081109
return _contract.storageLayout.init([&]{ return StorageLayout().generate(*_contract.contract, DataLocation::Storage); });
11091110
}
@@ -1523,6 +1524,7 @@ void CompilerStack::compileContract(
15231524
solAssert(!m_viaIR, "");
15241525
solUnimplementedAssert(!m_eofVersion.has_value(), "Experimental EOF support is only available for via-IR compilation.");
15251526
solAssert(m_stackState >= AnalysisSuccessful, "");
1527+
solUnimplementedAssert(!_contract.storageLayoutSpecifier(), "Code generation is not supported for contracts with specified storage layout base.");
15261528

15271529
if (_otherCompilers.count(&_contract))
15281530
return;
@@ -1558,7 +1560,7 @@ void CompilerStack::compileContract(
15581560
void CompilerStack::generateIR(ContractDefinition const& _contract, bool _unoptimizedOnly)
15591561
{
15601562
solAssert(m_stackState >= AnalysisSuccessful, "");
1561-
1563+
solUnimplementedAssert(!_contract.storageLayoutSpecifier(), "Code generation is not supported for contracts with specified storage layout base.");
15621564
Contract& compiledContract = m_contracts.at(_contract.fullyQualifiedName());
15631565
if (compiledContract.yulIR)
15641566
{

0 commit comments

Comments
 (0)