Skip to content
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: 2 additions & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@
Language Features:

Compiler Features:
* Custom Storage Layout: Allow using `constant` state variables in the base slot expression.
* ethdebug: Experimental support for instructions and source locations under EOF.


Bugfixes:
* Assembler: Fix not using a fixed-width type for IDs being assigned to subassemblies nested more than one level away, resulting in inconsistent `--asm-json` output between target architectures.
* Yul Optimizer: Fix edge case in which invalid Yul code is produced by ExpressionSimplifier due to expressions being substituted that contain out-of-scope variables.
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
73 changes: 60 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,64 @@ void PostTypeContractLevelChecker::checkStorageLayoutSpecifier(ContractDefinitio
}

auto const* baseSlotExpressionType = type(baseSlotExpression);
auto const* integerType = dynamic_cast<IntegerType const*>(baseSlotExpressionType);
auto const* rationalType = dynamic_cast<RationalNumberType const*>(baseSlotExpressionType);
if (!rationalType)
if (
!integerType &&
!rationalType
)
{
std::string errorMsg = "The base slot of the storage layout must evaluate to an integer.";
if (dynamic_cast<AddressType const*>(baseSlotExpressionType))
errorMsg += " The type of the supplied expression is address.";
else if (auto const* fixedBytesType = dynamic_cast<FixedBytesType const*>(baseSlotExpressionType))
errorMsg += fmt::format(
" The type of the supplied expression is bytes{}.",
fixedBytesType->numBytes()
)
;

m_errorReporter.typeError(
6396_error,
1763_error,
baseSlotExpression.location(),
"The base slot of the storage layout must evaluate to a rational number."
errorMsg
);
return;
}

if (rationalType->isFractional())
rational baseSlotRationalValue;
if (integerType)
{
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
{
solAssert(rationalType);
if (rationalType->isFractional())
{
m_errorReporter.typeError(
ErrorId{1763},
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 +173,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 1763: (21-36): The base slot of the storage layout must evaluate to an integer. The type of the supplied expression is address.
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
address constant x = 0xdCad3a6d3569DF655070DEd06cb7A1b2Ccd1D3AF;
contract C layout at x {

}
// ----
// TypeError 1763: (86-87): The base slot of the storage layout must evaluate to an integer. The type of the supplied expression is address.
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 1763: (21-30): The base slot of the storage layout must evaluate to an integer.
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 1763: (46-47): The base slot of the storage layout must evaluate to an integer.
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 1763: (21-25): The base slot of the storage layout must evaluate to an integer.
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
bytes32 constant CONST1 = "12345";
contract A layout at CONST1 {}
contract C layout at CONST1[1] {}
// ----
// TypeError 1763: (56-62): The base slot of the storage layout must evaluate to an integer. The type of the supplied expression is bytes32.
// TypeError 1763: (87-96): The base slot of the storage layout must evaluate to an integer. The type of the supplied expression is bytes1.
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
@@ -0,0 +1,7 @@
uint constant CONST1 = 1.23e100 / 2e50;
contract C layout at CONST1 {}
uint constant CONST2 = 2**256 * (500e-3);
contract D layout at CONST2 {}
uint constant CONST3 = (2**255 * 2) - (2**256 + 1) + 1;
contract E layout at CONST3 {}
// ----
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 1763: (52-61): The base slot of the storage layout must evaluate to an integer.
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 1763: (71-74): The base slot of the storage layout must evaluate to an integer.
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 1763: (21-63): The base slot of the storage layout must evaluate to an integer. The type of the supplied expression is address.
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 1763: (21-32): The base slot of the storage layout must evaluate to an integer.
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 1763: (51-55): The base slot of the storage layout must evaluate to an integer. The type of the supplied expression is bytes1.
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 1763: (B:46-54): The base slot of the storage layout must evaluate to an integer.
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 1763: (35-53): The base slot of the storage layout must evaluate to an integer.
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 1763: (21-25): The base slot of the storage layout must evaluate to an integer.
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,4 @@
contract A layout at 42.0 {}
contract B layout at 2.5e10 {}
contract C layout at 12/3 {}
// ----
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 1763: (21-35): The base slot of the storage layout must evaluate to an integer.
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 1763: (21-30): The base slot of the storage layout must evaluate to an integer.
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 1763: (82-83): The base slot of the storage layout must evaluate to an integer.
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 1763: (45-60): The base slot of the storage layout must evaluate to an integer.
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.