Skip to content

Support constants in custom storage layout expression #15944

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

Open
wants to merge 2 commits into
base: develop
Choose a base branch
from
Open
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
2 changes: 1 addition & 1 deletion Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Language Features:


Compiler Features:

* Custom Storage Layout: the base slot expression can also be specified by constant variables.

Bugfixes:

Expand Down
2 changes: 1 addition & 1 deletion docs/contracts/custom-storage-layout.rst
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ As the above example shows, the specifier uses the ``layout at <base-slot-expres
and is located in the header of a contract definition.

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

A custom layout cannot make contract's storage "wrap around".
Expand Down
62 changes: 49 additions & 13 deletions libsolidity/analysis/PostTypeContractLevelChecker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include <libsolidity/analysis/PostTypeContractLevelChecker.h>

#include <fmt/format.h>
#include <libsolidity/analysis/ConstantEvaluator.h>
#include <libsolidity/ast/AST.h>
#include <libsolidity/ast/ASTUtils.h>
#include <libsolidity/ast/TypeProvider.h>
Expand Down Expand Up @@ -101,29 +102,53 @@ void PostTypeContractLevelChecker::checkStorageLayoutSpecifier(ContractDefinitio
}

auto const* baseSlotExpressionType = type(baseSlotExpression);
auto const* rationalType = dynamic_cast<RationalNumberType const*>(baseSlotExpressionType);
if (!rationalType)
if (
!dynamic_cast<IntegerType const*>(baseSlotExpressionType) &&
!dynamic_cast<RationalNumberType const*>(baseSlotExpressionType)
)
{
m_errorReporter.typeError(
6396_error,
baseSlotExpression.location(),
"The base slot of the storage layout must evaluate to a rational number."
"The base slot of the storage layout must evaluate to an integer number."
);
return;
}

if (rationalType->isFractional())
rational baseSlotRationalValue;
if (auto const integerType = dynamic_cast<IntegerType const*>(baseSlotExpressionType))
{
m_errorReporter.typeError(
1763_error,
baseSlotExpression.location(),
"The base slot of the storage layout must evaluate to an integer."
);
return;
std::optional<ConstantEvaluator::TypedRational> typedRational = ConstantEvaluator::evaluate(m_errorReporter, baseSlotExpression);
if (!typedRational)
{
m_errorReporter.typeError(
1505_error,
baseSlotExpression.location(),
"The base slot expression contains elements that are not yet supported "
"by the internal constant evaluator and therefore cannot be evaluated at compilation time."
);
return;
}
baseSlotRationalValue = typedRational->value;
}
else
{
auto const* rationalType = dynamic_cast<RationalNumberType const*>(baseSlotExpressionType);
solAssert(rationalType);
if (rationalType->isFractional())
{
m_errorReporter.typeError(
1763_error,
baseSlotExpression.location(),
"The base slot of the storage layout must evaluate to an integer."
);
return;
}
baseSlotRationalValue = rationalType->value();
}
solAssert(rationalType->value().denominator() == 1);

bigint baseSlot = rationalType->value().numerator();
solAssert(baseSlotRationalValue.denominator() == 1);
bigint baseSlot = baseSlotRationalValue.numerator();
if (!(0 <= baseSlot && baseSlot <= std::numeric_limits<u256>::max()))
{
m_errorReporter.typeError(
Expand All @@ -137,7 +162,18 @@ void PostTypeContractLevelChecker::checkStorageLayoutSpecifier(ContractDefinitio
return;
}

solAssert(baseSlotExpressionType->isImplicitlyConvertibleTo(*TypeProvider::uint256()));
if (!baseSlotExpressionType->isImplicitlyConvertibleTo(*TypeProvider::uint256()))
{
m_errorReporter.typeError(
1481_error,
baseSlotExpression.location(),
fmt::format(
"The base slot expression type {} is not convertible to type uint256.",
baseSlotExpressionType->humanReadableName()
)
);
return;
}
storageLayoutSpecifier->annotation().baseSlot = u256(baseSlot);

bigint size = contractStorageSizeUpperBound(_contract, VariableDeclaration::Location::Unspecified);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
contract C layout at abi.decode(abi.encode(42), (uint)) {}
// ----
// TypeError 6396: (21-55): The base slot of the storage layout must evaluate to a rational number.
// TypeError 1505: (21-55): The base slot expression contains elements that are not yet supported by the internal constant evaluator and therefore cannot be evaluated at compilation time.
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
contract C layout at address(0x1234) {}
// ----
// TypeError 6396: (21-36): The base slot of the storage layout must evaluate to a rational number.
// TypeError 6396: (21-36): The base slot of the storage layout must evaluate to an integer number.
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
address constant x = 0xdCad3a6d3569DF655070DEd06cb7A1b2Ccd1D3AF;
contract C layout at x {

}
// ----
// TypeError 6396: (86-87): The base slot of the storage layout must evaluate to an integer number.
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
contract C layout at [1, 2, 3] {}
// ----
// TypeError 6396: (21-30): The base slot of the storage layout must evaluate to a rational number.
// TypeError 6396: (21-30): The base slot of the storage layout must evaluate to an integer number.
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
contract C layout at ~uint(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF) {}
// ----
// TypeError 6396: (21-94): The base slot of the storage layout must evaluate to a rational number.
// TypeError 1505: (21-94): The base slot expression contains elements that are not yet supported by the internal constant evaluator and therefore cannot be evaluated at compilation time.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
bool constant x = false;
contract C layout at x {}
// ----
// TypeError 6396: (46-47): The base slot of the storage layout must evaluate to an integer number.
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
contract C layout at true {}
// ----
// TypeError 6396: (21-25): The base slot of the storage layout must evaluate to a rational number.
// TypeError 6396: (21-25): The base slot of the storage layout must evaluate to an integer number.
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
bytes32 constant x = "ABC";
contract A layout at x {}
contract C layout at x[1] {}
// ----
// TypeError 6396: (49-50): The base slot of the storage layout must evaluate to an integer number.
// TypeError 6396: (75-79): The base slot of the storage layout must evaluate to an integer number.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
uint constant N = 100;
contract C layout at N / ~N {}
// ----
// TypeError 6396: (44-50): The base slot of the storage layout must evaluate to a rational number.
// TypeError 3667: (48-50): Arithmetic error when computing constant value.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
uint constant N = 100;
contract C layout at N / 0 {}
// ----
// TypeError 6396: (44-49): The base slot of the storage layout must evaluate to a rational number.
// TypeError 1505: (44-49): The base slot expression contains elements that are not yet supported by the internal constant evaluator and therefore cannot be evaluated at compilation time.
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ contract A {

contract C is A layout at A.x { }
// ----
// TypeError 6396: (68-71): The base slot of the storage layout must evaluate to a rational number.
// TypeError 1505: (68-71): The base slot expression contains elements that are not yet supported by the internal constant evaluator and therefore cannot be evaluated at compilation time.
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
==== Source: A ====
uint constant x = 77;

==== Source: B ====
import "A" as M;
contract C layout at M.x{ }
// ----
// TypeError 1505: (B:38-41): The base slot expression contains elements that are not yet supported by the internal constant evaluator and therefore cannot be evaluated at compilation time.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
uint constant x = addmod(10, 2, 8);
uint constant y = mulmod(10, 2, 8);
contract C layout at x {}
contract D layout at y {}
// ----
// TypeError 1505: (93-94): The base slot expression contains elements that are not yet supported by the internal constant evaluator and therefore cannot be evaluated at compilation time.
// TypeError 1505: (119-120): The base slot expression contains elements that are not yet supported by the internal constant evaluator and therefore cannot be evaluated at compilation time.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
uint constant x = uint(42);
contract C layout at x {}
// ----
// TypeError 1505: (49-50): The base slot expression contains elements that are not yet supported by the internal constant evaluator and therefore cannot be evaluated at compilation time.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
uint constant x = 42;
uint constant y = x * 2;
contract C layout at y {}
// ----
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
uint constant x = ((2**5 + 2**5) * (2 ** 10 + 1 << 1)) % 2**256 - 1;
contract C layout at x {}
// ----
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ enum Color {Red, Green, Blue}

contract C layout at Color.Red {}
// ----
// TypeError 6396: (52-61): The base slot of the storage layout must evaluate to a rational number.
// TypeError 6396: (52-61): The base slot of the storage layout must evaluate to an integer number.
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ contract A {

contract C layout at A.f { }
// ----
// TypeError 6396: (71-74): The base slot of the storage layout must evaluate to a rational number.
// TypeError 6396: (71-74): The base slot of the storage layout must evaluate to an integer number.
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ contract A {
}
contract C is A layout at uint32(this.f.selector) {}
// ----
// TypeError 6396: (68-91): The base slot of the storage layout must evaluate to a rational number.
// TypeError 1505: (68-91): The base slot expression contains elements that are not yet supported by the internal constant evaluator and therefore cannot be evaluated at compilation time.
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
contract C layout at 0xdCad3a6d3569DF655070DEd06cb7A1b2Ccd1D3AF {}
// ----
// TypeError 6396: (21-63): The base slot of the storage layout must evaluate to a rational number.
// TypeError 6396: (21-63): The base slot of the storage layout must evaluate to an integer number.
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
contract C layout at hex"616263" {}
// ----
// TypeError 6396: (21-32): The base slot of the storage layout must evaluate to a rational number.
// TypeError 6396: (21-32): The base slot of the storage layout must evaluate to an integer number.
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
contract at layout at uint40(bytes5(hex"0011223344")) { }
// ----
// TypeError 6396: (22-53): The base slot of the storage layout must evaluate to a rational number.
// TypeError 1505: (22-53): The base slot expression contains elements that are not yet supported by the internal constant evaluator and therefore cannot be evaluated at compilation time.
17 changes: 17 additions & 0 deletions test/libsolidity/syntaxTests/storageLayoutSpecifier/immutables.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
function f() returns (uint) {
return 2;
}

contract A {
address immutable a = 0x0000000000000000000000000000000000000001;
uint immutable x = 1; // considered pure by the compiler (initialized with a literal)
uint immutable y = f(); // considered not pure by the compiler (initialized with a function)
}

contract B is A layout at A.a { }
contract C is A layout at A.x { }
contract D is A layout at A.y { }
// ----
// TypeError 1139: (346-349): The base slot of the storage layout must be a compile-time constant expression.
// TypeError 1139: (380-383): The base slot of the storage layout must be a compile-time constant expression.
// TypeError 1139: (414-417): The base slot of the storage layout must be a compile-time constant expression.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
int constant x = -42;
int constant y = 64;
contract C layout at x {}
contract D layout at y {}
// ----
// TypeError 6753: (64-65): The base slot of the storage layout evaluates to -42, which is outside the range of type uint256.
// TypeError 1481: (90-91): The base slot expression type int256 is not convertible to type uint256.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
bytes32 constant b = "bytes";
contract A layout at b[1] {}
// ----
// TypeError 6396: (51-55): The base slot of the storage layout must evaluate to a rational number.
// TypeError 6396: (51-55): The base slot of the storage layout must evaluate to an integer number.
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
uint constant X = 42;
contract C layout at 0xffff * (50 - X) { }
// ----
// TypeError 6396: (43-60): The base slot of the storage layout must evaluate to a rational number.
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ interface I {}

contract C layout at uint(bytes32(type(I).interfaceId)) { }
// ----
// TypeError 6396: (37-71): The base slot of the storage layout must evaluate to a rational number.
// TypeError 1505: (37-71): The base slot expression contains elements that are not yet supported by the internal constant evaluator and therefore cannot be evaluated at compilation time.
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@ import "A" as MyModule;

contract C layout at MyModule {}
// ----
// TypeError 6396: (B:46-54): The base slot of the storage layout must evaluate to a rational number.
// TypeError 6396: (B:46-54): The base slot of the storage layout must evaluate to an integer number.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
contract A {}
contract C layout at A(address(0x1234)) {}
// ----
// TypeError 6396: (35-53): The base slot of the storage layout must evaluate to a rational number.
// TypeError 6396: (35-53): The base slot of the storage layout must evaluate to an integer number.
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
contract A layout at uint {}
// ----
// TypeError 6396: (21-25): The base slot of the storage layout must evaluate to a rational number.
// TypeError 6396: (21-25): The base slot of the storage layout must evaluate to an integer number.
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
contract at layout at uint(42) { }
// ----
// TypeError 6396: (22-30): The base slot of the storage layout must evaluate to a rational number.
// TypeError 1505: (22-30): The base slot expression contains elements that are not yet supported by the internal constant evaluator and therefore cannot be evaluated at compilation time.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
contract A layout at addmod(1, 2, 3) {}
contract B layout at mulmod(3, 2, 1) {}
// ----
// TypeError 6396: (21-36): The base slot of the storage layout must evaluate to a rational number.
// TypeError 6396: (61-76): The base slot of the storage layout must evaluate to a rational number.
// TypeError 1505: (21-36): The base slot expression contains elements that are not yet supported by the internal constant evaluator and therefore cannot be evaluated at compilation time.
// TypeError 1505: (61-76): The base slot expression contains elements that are not yet supported by the internal constant evaluator and therefore cannot be evaluated at compilation time.
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
contract C layout at "MyLayoutBase" {}
// ----
// TypeError 6396: (21-35): The base slot of the storage layout must evaluate to a rational number.
// TypeError 6396: (21-35): The base slot of the storage layout must evaluate to an integer number.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
contract A layout at true ? 42 : 94 {}
contract B layout at 255 + (true ? 1 : 0) {}
// ----
// TypeError 6396: (21-35): The base slot of the storage layout must evaluate to a rational number.
// TypeError 6396: (60-80): The base slot of the storage layout must evaluate to a rational number.
// TypeError 1505: (21-35): The base slot expression contains elements that are not yet supported by the internal constant evaluator and therefore cannot be evaluated at compilation time.
// TypeError 1505: (60-80): The base slot expression contains elements that are not yet supported by the internal constant evaluator and therefore cannot be evaluated at compilation time.
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
contract C layout at (1, 2, 3) {}
// ----
// TypeError 6396: (21-30): The base slot of the storage layout must evaluate to a rational number.
// TypeError 6396: (21-30): The base slot of the storage layout must evaluate to an integer number.
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
contract at layout at type(uint).max { }
// ----
// TypeError 6396: (22-36): The base slot of the storage layout must evaluate to a rational number.
// TypeError 1505: (22-36): The base slot expression contains elements that are not yet supported by the internal constant evaluator and therefore cannot be evaluated at compilation time.
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ type MyUint is uint128;
MyUint constant x = MyUint.wrap(42);
contract C layout at x {}
// ----
// TypeError 6396: (82-83): The base slot of the storage layout must evaluate to a rational number.
// TypeError 6396: (82-83): The base slot of the storage layout must evaluate to an integer number.
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ type MyUint is uint128;
MyUint constant x = MyUint.wrap(42);
contract C layout at MyUint.unwrap(x) {}
// ----
// TypeError 6396: (82-98): The base slot of the storage layout must evaluate to a rational number.
// TypeError 1505: (82-98): The base slot expression contains elements that are not yet supported by the internal constant evaluator and therefore cannot be evaluated at compilation time.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
type MyUint is uint128;
contract C layout at MyUint.wrap(42) {}
// ----
// TypeError 6396: (45-60): The base slot of the storage layout must evaluate to a rational number.
// TypeError 6396: (45-60): The base slot of the storage layout must evaluate to an integer number.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
contract A layout at [1, 2, 3][0] {}
contract B layout at 255 + [1, 2, 3][0] {}
// ----
// TypeError 6396: (21-33): The base slot of the storage layout must evaluate to a rational number.
// TypeError 6396: (58-76): The base slot of the storage layout must evaluate to a rational number.
// TypeError 1505: (21-33): The base slot expression contains elements that are not yet supported by the internal constant evaluator and therefore cannot be evaluated at compilation time.
// TypeError 1505: (58-76): The base slot expression contains elements that are not yet supported by the internal constant evaluator and therefore cannot be evaluated at compilation time.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
bytes32 constant b = "Solidity";
contract C layout at uint8(b[1]) {}
// ----
// TypeError 6396: (54-65): The base slot of the storage layout must evaluate to a rational number.
// TypeError 1505: (54-65): The base slot expression contains elements that are not yet supported by the internal constant evaluator and therefore cannot be evaluated at compilation time.