Skip to content

Commit 64e46af

Browse files
Allow constants in custom storage layout expression
1 parent 6d6bb63 commit 64e46af

File tree

3 files changed

+52
-14
lines changed

3 files changed

+52
-14
lines changed

Changelog.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@
33
Language Features:
44

55
Compiler Features:
6+
* Custom Storage Layout: the base slot expression can also be specified by constant variables.
67
* ethdebug: Experimental support for instructions and source locations under EOF.
78

9+
810
Bugfixes:
911

1012
### 0.8.30 (2025-05-07)

docs/contracts/custom-storage-layout.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ As the above example shows, the specifier uses the ``layout at <base-slot-expres
2323
and is located in the header of a contract definition.
2424

2525
The layout specifier can be placed either before or after the inheritance specifier, and can appear at most once.
26-
The ``base-slot-expression`` must be an :ref:`integer literal<rational_literals>` expression
26+
The ``base-slot-expression`` must be an :ref:`integer literal<rational_literals>` expression or a constant
2727
that can be evaluated at compilation time and yields a value in the range of ``uint256``.
2828

2929
A custom layout cannot make contract's storage "wrap around".

libsolidity/analysis/PostTypeContractLevelChecker.cpp

Lines changed: 49 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include <libsolidity/analysis/PostTypeContractLevelChecker.h>
2424

2525
#include <fmt/format.h>
26+
#include <libsolidity/analysis/ConstantEvaluator.h>
2627
#include <libsolidity/ast/AST.h>
2728
#include <libsolidity/ast/ASTUtils.h>
2829
#include <libsolidity/ast/TypeProvider.h>
@@ -101,29 +102,53 @@ void PostTypeContractLevelChecker::checkStorageLayoutSpecifier(ContractDefinitio
101102
}
102103

103104
auto const* baseSlotExpressionType = type(baseSlotExpression);
104-
auto const* rationalType = dynamic_cast<RationalNumberType const*>(baseSlotExpressionType);
105-
if (!rationalType)
105+
if (
106+
!dynamic_cast<IntegerType const*>(baseSlotExpressionType) &&
107+
!dynamic_cast<RationalNumberType const*>(baseSlotExpressionType)
108+
)
106109
{
107110
m_errorReporter.typeError(
108111
6396_error,
109112
baseSlotExpression.location(),
110-
"The base slot of the storage layout must evaluate to a rational number."
113+
"The base slot of the storage layout must evaluate to an integer number."
111114
);
112115
return;
113116
}
114117

115-
if (rationalType->isFractional())
118+
rational baseSlotRationalValue;
119+
if (auto const integerType = dynamic_cast<IntegerType const*>(baseSlotExpressionType))
116120
{
117-
m_errorReporter.typeError(
118-
1763_error,
119-
baseSlotExpression.location(),
120-
"The base slot of the storage layout must evaluate to an integer."
121-
);
122-
return;
121+
std::optional<ConstantEvaluator::TypedRational> typedRational = ConstantEvaluator::evaluate(m_errorReporter, baseSlotExpression);
122+
if (!typedRational)
123+
{
124+
m_errorReporter.typeError(
125+
1505_error,
126+
baseSlotExpression.location(),
127+
"The base slot expression contains elements that are not yet supported "
128+
"by the internal constant evaluator and therefore cannot be evaluated at compilation time."
129+
);
130+
return;
131+
}
132+
baseSlotRationalValue = typedRational->value;
133+
}
134+
else
135+
{
136+
auto const* rationalType = dynamic_cast<RationalNumberType const*>(baseSlotExpressionType);
137+
solAssert(rationalType);
138+
if (rationalType->isFractional())
139+
{
140+
m_errorReporter.typeError(
141+
1763_error,
142+
baseSlotExpression.location(),
143+
"The base slot of the storage layout must evaluate to an integer."
144+
);
145+
return;
146+
}
147+
baseSlotRationalValue = rationalType->value();
123148
}
124-
solAssert(rationalType->value().denominator() == 1);
125149

126-
bigint baseSlot = rationalType->value().numerator();
150+
solAssert(baseSlotRationalValue.denominator() == 1);
151+
bigint baseSlot = baseSlotRationalValue.numerator();
127152
if (!(0 <= baseSlot && baseSlot <= std::numeric_limits<u256>::max()))
128153
{
129154
m_errorReporter.typeError(
@@ -137,7 +162,18 @@ void PostTypeContractLevelChecker::checkStorageLayoutSpecifier(ContractDefinitio
137162
return;
138163
}
139164

140-
solAssert(baseSlotExpressionType->isImplicitlyConvertibleTo(*TypeProvider::uint256()));
165+
if (!baseSlotExpressionType->isImplicitlyConvertibleTo(*TypeProvider::uint256()))
166+
{
167+
m_errorReporter.typeError(
168+
1481_error,
169+
baseSlotExpression.location(),
170+
fmt::format(
171+
"The base slot expression type {} is not convertible to type uint256.",
172+
baseSlotExpressionType->humanReadableName()
173+
)
174+
);
175+
return;
176+
}
141177
storageLayoutSpecifier->annotation().baseSlot = u256(baseSlot);
142178

143179
bigint size = contractStorageSizeUpperBound(_contract, VariableDeclaration::Location::Unspecified);

0 commit comments

Comments
 (0)