Skip to content

Commit 6ef580c

Browse files
authored
Merge pull request ethereum#15257 from ethereum/transientStorageLegacyCodegen
Support transient storage state variables in legacy codegen
2 parents e629a03 + 9d7cc42 commit 6ef580c

File tree

64 files changed

+614
-73
lines changed

Some content is hidden

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

64 files changed

+614
-73
lines changed

Changelog.md

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ Language Features:
44

55

66
Compiler Features:
7+
* Code Generator: Transient storage value type state variables are now supported by the legacy pipeline.
78

89

910
Bugfixes:

libsolidity/analysis/DeclarationTypeChecker.cpp

+7
Original file line numberDiff line numberDiff line change
@@ -398,6 +398,13 @@ void DeclarationTypeChecker::endVisit(VariableDeclaration const& _variable)
398398
Location varLoc = _variable.referenceLocation();
399399
DataLocation typeLoc = DataLocation::Memory;
400400

401+
if (varLoc == VariableDeclaration::Location::Transient && !m_evmVersion.supportsTransientStorage())
402+
m_errorReporter.declarationError(
403+
7985_error,
404+
_variable.location(),
405+
"Transient storage is not supported by EVM versions older than cancun."
406+
);
407+
401408
std::set<Location> allowedDataLocations = _variable.allowedDataLocations();
402409
if (!allowedDataLocations.count(varLoc))
403410
{

libsolidity/analysis/ReferencesResolver.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -370,7 +370,7 @@ void ReferencesResolver::operator()(yul::Identifier const& _identifier)
370370
m_errorReporter.declarationError(
371371
9467_error,
372372
nativeLocationOf(_identifier),
373-
"Identifier not found. Use \".slot\" and \".offset\" to access storage variables."
373+
"Identifier not found. Use \".slot\" and \".offset\" to access storage or transient storage variables."
374374
);
375375
return;
376376
}

libsolidity/analysis/TypeChecker.cpp

+3-3
Original file line numberDiff line numberDiff line change
@@ -785,7 +785,7 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly)
785785
}
786786
else if (identifierInfo.suffix == "slot" || identifierInfo.suffix == "offset")
787787
{
788-
m_errorReporter.typeError(6617_error, nativeLocationOf(_identifier), "The suffixes .offset and .slot can only be used on non-constant storage variables.");
788+
m_errorReporter.typeError(6617_error, nativeLocationOf(_identifier), "The suffixes .offset and .slot can only be used on non-constant storage or transient storage variables.");
789789
return false;
790790
}
791791
else if (var && var->value() && !var->value()->annotation().type && !dynamic_cast<Literal const*>(var->value().get()))
@@ -824,7 +824,7 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly)
824824
{
825825
if (var->isStateVariable())
826826
{
827-
m_errorReporter.typeError(4713_error, nativeLocationOf(_identifier), "State variables cannot be assigned to - you have to use \"sstore()\".");
827+
m_errorReporter.typeError(4713_error, nativeLocationOf(_identifier), "State variables cannot be assigned to - you have to use \"sstore()\" or \"tstore()\".");
828828
return false;
829829
}
830830
else if (suffix != "slot")
@@ -869,7 +869,7 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly)
869869
m_errorReporter.typeError(
870870
1408_error,
871871
nativeLocationOf(_identifier),
872-
"Only local variables are supported. To access storage variables, use the \".slot\" and \".offset\" suffixes."
872+
"Only local variables are supported. To access state variables, use the \".slot\" and \".offset\" suffixes."
873873
);
874874
return false;
875875
}

libsolidity/ast/Types.cpp

+12-17
Original file line numberDiff line numberDiff line change
@@ -2140,30 +2140,25 @@ FunctionType const* ContractType::newExpressionType() const
21402140
return m_constructorType;
21412141
}
21422142

2143-
std::vector<std::tuple<VariableDeclaration const*, u256, unsigned>> ContractType::stateVariables(std::optional<DataLocation> _location) const
2143+
std::vector<std::tuple<VariableDeclaration const*, u256, unsigned>> ContractType::stateVariables(DataLocation _location) const
21442144
{
2145-
std::optional<VariableDeclaration::Location> location;
2146-
if (_location.has_value())
2145+
VariableDeclaration::Location location;
2146+
switch (_location)
21472147
{
2148-
switch (_location.value())
2149-
{
2150-
case DataLocation::Storage:
2151-
location = std::make_optional(VariableDeclaration::Location::Unspecified); // By default state variables have unspecified (storage) data location
2152-
break;
2153-
case DataLocation::Transient:
2154-
location = std::make_optional(VariableDeclaration::Location::Transient);
2155-
break;
2156-
default: solAssert(false);
2157-
}
2148+
case DataLocation::Storage:
2149+
location = VariableDeclaration::Location::Unspecified;
2150+
break;
2151+
case DataLocation::Transient:
2152+
location = VariableDeclaration::Location::Transient;
2153+
break;
2154+
default:
2155+
solAssert(false);
21582156
}
21592157

21602158
std::vector<VariableDeclaration const*> variables;
21612159
for (ContractDefinition const* contract: m_contract.annotation().linearizedBaseContracts | ranges::views::reverse)
21622160
for (VariableDeclaration const* variable: contract->stateVariables())
2163-
if (
2164-
!(variable->isConstant() || variable->immutable()) &&
2165-
variable->referenceLocation() == location.value_or(variable->referenceLocation())
2166-
)
2161+
if (!(variable->isConstant() || variable->immutable()) && variable->referenceLocation() == location)
21672162
variables.push_back(variable);
21682163
TypePointers types;
21692164
for (auto variable: variables)

libsolidity/ast/Types.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -1002,7 +1002,7 @@ class ContractType: public Type
10021002

10031003
/// @returns a list of all state variables (including inherited) of the contract and their
10041004
/// offsets in storage/transient storage.
1005-
std::vector<std::tuple<VariableDeclaration const*, u256, unsigned>> stateVariables(std::optional<DataLocation> _location = std::nullopt) const;
1005+
std::vector<std::tuple<VariableDeclaration const*, u256, unsigned>> stateVariables(DataLocation _location) const;
10061006
/// @returns a list of all immutable variables (including inherited) of the contract.
10071007
std::vector<VariableDeclaration const*> immutableVariables() const;
10081008
protected:

libsolidity/codegen/Compiler.cpp

+8-6
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
#include <libsolidity/codegen/ContractCompiler.h>
2727
#include <libevmasm/Assembly.h>
2828

29-
#include <range/v3/algorithm/all_of.hpp>
29+
#include <range/v3/algorithm/none_of.hpp>
3030

3131
using namespace solidity;
3232
using namespace solidity::frontend;
@@ -37,14 +37,16 @@ void Compiler::compileContract(
3737
bytes const& _metadata
3838
)
3939
{
40-
auto static notTransient = [](VariableDeclaration const* _varDeclaration) {
41-
solAssert(_varDeclaration);
42-
return _varDeclaration->referenceLocation() != VariableDeclaration::Location::Transient;
40+
auto static isTransientReferenceType = [](VariableDeclaration const* _varDeclaration) {
41+
solAssert(_varDeclaration && _varDeclaration->type());
42+
return
43+
_varDeclaration->referenceLocation() == VariableDeclaration::Location::Transient &&
44+
!_varDeclaration->type()->isValueType();
4345
};
4446

4547
solUnimplementedAssert(
46-
ranges::all_of(_contract.stateVariables(), notTransient),
47-
"Transient storage variables are not supported."
48+
ranges::none_of(_contract.stateVariables(), isTransientReferenceType),
49+
"Transient storage reference type variables are not supported."
4850
);
4951

5052
ContractCompiler runtimeCompiler(nullptr, m_runtimeContext, m_optimiserSettings);

libsolidity/codegen/ContractCompiler.cpp

+4-4
Original file line numberDiff line numberDiff line change
@@ -570,8 +570,9 @@ void ContractCompiler::appendReturnValuePacker(TypePointers const& _typeParamete
570570

571571
void ContractCompiler::registerStateVariables(ContractDefinition const& _contract)
572572
{
573-
for (auto const& var: ContractType(_contract).stateVariables())
574-
m_context.addStateVariable(*std::get<0>(var), std::get<1>(var), std::get<2>(var));
573+
for (auto const location: {DataLocation::Storage, DataLocation::Transient})
574+
for (auto const& var: ContractType(_contract).stateVariables(location))
575+
m_context.addStateVariable(*std::get<0>(var), std::get<1>(var), std::get<2>(var));
575576
}
576577

577578
void ContractCompiler::registerImmutableVariables(ContractDefinition const& _contract)
@@ -586,7 +587,7 @@ void ContractCompiler::initializeStateVariables(ContractDefinition const& _contr
586587
solAssert(!_contract.isLibrary(), "Tried to initialize state variables of library.");
587588
for (VariableDeclaration const* variable: _contract.stateVariables())
588589
{
589-
solUnimplementedAssert(variable->referenceLocation() != VariableDeclaration::Location::Transient, "Transient storage variables not supported.");
590+
solAssert(variable->referenceLocation() != VariableDeclaration::Location::Transient || !variable->value());
590591
if (variable->value() && !variable->isConstant())
591592
ExpressionCompiler(m_context, m_optimiserSettings.runOrderLiterals).appendStateVariableInitialization(*variable);
592593
}
@@ -595,7 +596,6 @@ void ContractCompiler::initializeStateVariables(ContractDefinition const& _contr
595596
bool ContractCompiler::visit(VariableDeclaration const& _variableDeclaration)
596597
{
597598
solAssert(_variableDeclaration.isStateVariable(), "Compiler visit to non-state variable declaration.");
598-
solUnimplementedAssert(_variableDeclaration.referenceLocation() != VariableDeclaration::Location::Transient, "Transient storage variables not supported.");
599599
CompilerContext::LocationSetter locationSetter(m_context, _variableDeclaration);
600600

601601
m_context.startFunction(_variableDeclaration);

libsolidity/codegen/ExpressionCompiler.cpp

+15-1
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,10 @@ void ExpressionCompiler::appendStateVariableInitialization(VariableDeclaration c
108108
if (_varDecl.immutable())
109109
ImmutableItem(m_context, _varDecl).storeValue(*type, _varDecl.location(), true);
110110
else
111+
{
112+
solAssert(_varDecl.referenceLocation() != VariableDeclaration::Location::Transient);
111113
StorageItem(m_context, _varDecl).storeValue(*type, _varDecl.location(), true);
114+
}
112115
}
113116

114117
void ExpressionCompiler::appendConstStateVariableAccessor(VariableDeclaration const& _varDecl)
@@ -147,6 +150,7 @@ void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const&
147150
if (auto mappingType = dynamic_cast<MappingType const*>(returnType))
148151
{
149152
solAssert(CompilerUtils::freeMemoryPointer >= 0x40, "");
153+
solAssert(_varDecl.referenceLocation() != VariableDeclaration::Location::Transient);
150154

151155
// pop offset
152156
m_context << Instruction::POP;
@@ -196,6 +200,8 @@ void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const&
196200
}
197201
else if (auto arrayType = dynamic_cast<ArrayType const*>(returnType))
198202
{
203+
solAssert(_varDecl.referenceLocation() != VariableDeclaration::Location::Transient);
204+
199205
// pop offset
200206
m_context << Instruction::POP;
201207
utils().copyToStackTop(static_cast<unsigned>(paramTypes.size() - i + 1), 1);
@@ -229,6 +235,7 @@ void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const&
229235
solAssert(returnTypes.size() >= 1, "");
230236
if (StructType const* structType = dynamic_cast<StructType const*>(returnType))
231237
{
238+
solAssert(_varDecl.referenceLocation() != VariableDeclaration::Location::Transient);
232239
solAssert(!_varDecl.immutable(), "");
233240
// remove offset
234241
m_context << Instruction::POP;
@@ -258,6 +265,8 @@ void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const&
258265
solAssert(returnTypes.size() == 1, "");
259266
if (_varDecl.immutable())
260267
ImmutableItem(m_context, _varDecl).retrieveValue(SourceLocation());
268+
else if (_varDecl.referenceLocation() == VariableDeclaration::Location::Transient)
269+
TransientStorageItem(m_context, *returnType).retrieveValue(SourceLocation(), true);
261270
else
262271
StorageItem(m_context, *returnType).retrieveValue(SourceLocation(), true);
263272
utils().convertType(*returnType, *returnTypes.front());
@@ -2995,7 +3004,12 @@ void ExpressionCompiler::setLValueFromDeclaration(Declaration const& _declaratio
29953004
if (m_context.isLocalVariable(&_declaration))
29963005
setLValue<StackVariable>(_expression, dynamic_cast<VariableDeclaration const&>(_declaration));
29973006
else if (m_context.isStateVariable(&_declaration))
2998-
setLValue<StorageItem>(_expression, dynamic_cast<VariableDeclaration const&>(_declaration));
3007+
{
3008+
if (dynamic_cast<VariableDeclaration const&>(_declaration).referenceLocation() == VariableDeclaration::Location::Transient)
3009+
setLValue<TransientStorageItem>(_expression, dynamic_cast<VariableDeclaration const&>(_declaration));
3010+
else
3011+
setLValue<StorageItem>(_expression, dynamic_cast<VariableDeclaration const&>(_declaration));
3012+
}
29993013
else
30003014
solAssert(false, "Identifier type not supported or identifier not found.");
30013015
}

libsolidity/codegen/LValue.cpp

+27-16
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@ using namespace solidity::frontend;
3636
using namespace solidity::langutil;
3737
using namespace solidity::util;
3838

39-
4039
StackVariable::StackVariable(CompilerContext& _compilerContext, VariableDeclaration const& _declaration):
4140
LValue(_compilerContext, _declaration.annotation().type),
4241
m_baseStackOffset(m_context.baseStackOffsetOfVariable(_declaration)),
@@ -198,15 +197,17 @@ void ImmutableItem::setToZero(SourceLocation const&, bool _removeReference) cons
198197
m_context << Instruction::POP;
199198
}
200199

201-
StorageItem::StorageItem(CompilerContext& _compilerContext, VariableDeclaration const& _declaration):
202-
StorageItem(_compilerContext, *_declaration.annotation().type)
200+
template<bool IsTransient>
201+
GenericStorageItem<IsTransient>::GenericStorageItem(CompilerContext& _compilerContext, VariableDeclaration const& _declaration):
202+
GenericStorageItem<IsTransient>(_compilerContext, *_declaration.annotation().type)
203203
{
204204
solAssert(!_declaration.immutable(), "");
205205
auto const& location = m_context.storageLocationOfVariable(_declaration);
206206
m_context << location.first << u256(location.second);
207207
}
208208

209-
StorageItem::StorageItem(CompilerContext& _compilerContext, Type const& _type):
209+
template<bool IsTransient>
210+
GenericStorageItem<IsTransient>::GenericStorageItem(CompilerContext& _compilerContext, Type const& _type):
210211
LValue(_compilerContext, &_type)
211212
{
212213
if (m_dataType->isValueType())
@@ -217,11 +218,13 @@ StorageItem::StorageItem(CompilerContext& _compilerContext, Type const& _type):
217218
}
218219
}
219220

220-
void StorageItem::retrieveValue(SourceLocation const&, bool _remove) const
221+
template<bool IsTransient>
222+
void GenericStorageItem<IsTransient>::retrieveValue(langutil::SourceLocation const&, bool _remove) const
221223
{
222224
// stack: storage_key storage_offset
223225
if (!m_dataType->isValueType())
224226
{
227+
solUnimplementedAssert(!IsTransient, "Transient storage reference types are not supported yet.");
225228
solAssert(m_dataType->sizeOnStack() == 1, "Invalid storage ref size.");
226229
if (_remove)
227230
m_context << Instruction::POP; // remove byte offset
@@ -232,20 +235,20 @@ void StorageItem::retrieveValue(SourceLocation const&, bool _remove) const
232235
if (!_remove)
233236
CompilerUtils(m_context).copyToStackTop(sizeOnStack(), sizeOnStack());
234237
if (m_dataType->storageBytes() == 32)
235-
m_context << Instruction::POP << Instruction::SLOAD;
238+
m_context << Instruction::POP << s_loadInstruction;
236239
else
237240
{
238241
Type const* type = m_dataType;
239242
if (type->category() == Type::Category::UserDefinedValueType)
240243
type = type->encodingType();
241244
bool cleaned = false;
242245
m_context
243-
<< Instruction::SWAP1 << Instruction::SLOAD << Instruction::SWAP1
246+
<< Instruction::SWAP1 << s_loadInstruction << Instruction::SWAP1
244247
<< u256(0x100) << Instruction::EXP << Instruction::SWAP1 << Instruction::DIV;
245248
if (type->category() == Type::Category::FixedPoint)
246249
// implementation should be very similar to the integer case.
247250
solUnimplemented("Not yet implemented - FixedPointType.");
248-
else if (FunctionType const* fun = dynamic_cast<decltype(fun)>(type))
251+
else if (auto const* fun = dynamic_cast<FunctionType const*>(type))
249252
{
250253
if (fun->kind() == FunctionType::Kind::External)
251254
{
@@ -281,7 +284,8 @@ void StorageItem::retrieveValue(SourceLocation const&, bool _remove) const
281284
}
282285
}
283286

284-
void StorageItem::storeValue(Type const& _sourceType, SourceLocation const& _location, bool _move) const
287+
template<bool IsTransient>
288+
void GenericStorageItem<IsTransient>::storeValue(Type const& _sourceType, langutil::SourceLocation const& _location, bool _move) const
285289
{
286290
CompilerUtils utils(m_context);
287291
solAssert(m_dataType, "");
@@ -303,15 +307,15 @@ void StorageItem::storeValue(Type const& _sourceType, SourceLocation const& _loc
303307
utils.convertType(_sourceType, *m_dataType, true);
304308
m_context << Instruction::SWAP1;
305309

306-
m_context << Instruction::SSTORE;
310+
m_context << s_storeInstruction;
307311
}
308312
else
309313
{
310314
// OR the value into the other values in the storage slot
311315
m_context << u256(0x100) << Instruction::EXP;
312316
// stack: value storage_ref multiplier
313317
// fetch old value
314-
m_context << Instruction::DUP2 << Instruction::SLOAD;
318+
m_context << Instruction::DUP2 << s_loadInstruction;
315319
// stack: value storage_ref multiplier old_full_value
316320
// clear bytes in old value
317321
m_context
@@ -358,13 +362,14 @@ void StorageItem::storeValue(Type const& _sourceType, SourceLocation const& _loc
358362
}
359363
m_context << Instruction::MUL << Instruction::OR;
360364
// stack: value storage_ref updated_value
361-
m_context << Instruction::SWAP1 << Instruction::SSTORE;
365+
m_context << Instruction::SWAP1 << s_storeInstruction;
362366
if (_move)
363367
utils.popStackElement(*m_dataType);
364368
}
365369
}
366370
else
367371
{
372+
solUnimplementedAssert(!IsTransient, "Transient storage reference types are not supported yet.");
368373
solAssert(
369374
_sourceType.category() == m_dataType->category(),
370375
"Wrong type conversation for assignment."
@@ -445,16 +450,19 @@ void StorageItem::storeValue(Type const& _sourceType, SourceLocation const& _loc
445450
}
446451
}
447452

448-
void StorageItem::setToZero(SourceLocation const&, bool _removeReference) const
453+
template<bool IsTransient>
454+
void GenericStorageItem<IsTransient>::setToZero(langutil::SourceLocation const&, bool _removeReference) const
449455
{
450456
if (m_dataType->category() == Type::Category::Array)
451457
{
458+
solUnimplementedAssert(!IsTransient, "Transient storage reference types are not supported yet.");
452459
if (!_removeReference)
453460
CompilerUtils(m_context).copyToStackTop(sizeOnStack(), sizeOnStack());
454461
ArrayUtils(m_context).clearArray(dynamic_cast<ArrayType const&>(*m_dataType));
455462
}
456463
else if (m_dataType->category() == Type::Category::Struct)
457464
{
465+
solUnimplementedAssert(!IsTransient, "Transient storage reference types are not supported yet.");
458466
// stack layout: storage_key storage_offset
459467
// @todo this can be improved: use StorageItem for non-value types, and just store 0 in
460468
// all slots that contain value types later.
@@ -484,22 +492,22 @@ void StorageItem::setToZero(SourceLocation const&, bool _removeReference) const
484492
// offset should be zero
485493
m_context
486494
<< Instruction::POP << u256(0)
487-
<< Instruction::SWAP1 << Instruction::SSTORE;
495+
<< Instruction::SWAP1 << s_storeInstruction;
488496
}
489497
else
490498
{
491499
m_context << u256(0x100) << Instruction::EXP;
492500
// stack: storage_ref multiplier
493501
// fetch old value
494-
m_context << Instruction::DUP2 << Instruction::SLOAD;
502+
m_context << Instruction::DUP2 << s_loadInstruction;
495503
// stack: storage_ref multiplier old_full_value
496504
// clear bytes in old value
497505
m_context
498506
<< Instruction::SWAP1 << ((u256(1) << (8 * m_dataType->storageBytes())) - 1)
499507
<< Instruction::MUL;
500508
m_context << Instruction::NOT << Instruction::AND;
501509
// stack: storage_ref cleared_value
502-
m_context << Instruction::SWAP1 << Instruction::SSTORE;
510+
m_context << Instruction::SWAP1 << s_storeInstruction;
503511
}
504512
}
505513
}
@@ -610,3 +618,6 @@ void TupleObject::setToZero(SourceLocation const&, bool) const
610618
{
611619
solAssert(false, "Tried to delete tuple.");
612620
}
621+
622+
template class solidity::frontend::GenericStorageItem<false>;
623+
template class solidity::frontend::GenericStorageItem<true>;

0 commit comments

Comments
 (0)