Skip to content
Merged
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 @@ -4,6 +4,7 @@ Language Features:
* Yul: Introduce builtin `clz(x)` for counting the number of leading zero bits in a 256-bit word.

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.
* EVM: Set default EVM Version to `osaka`.
* DocString Parser: Warn about deprecation of inline assembly special comment `memory-safe-assembly`.
Expand All @@ -12,6 +13,7 @@ Compiler Features:
* Type Checker: Warn about deprecation of `send` and `transfer` functions on instances of `address`.
* Type Checker: Warn about deprecation of comparisons between variables of contract types.


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
1 change: 1 addition & 0 deletions docs/contracts/custom-storage-layout.rst
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ 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
that can be evaluated at compilation time and yields a value in the range of ``uint256``.
The use of constants initialized using such expressions is also allowed.

A custom layout cannot make contract's storage "wrap around".
If the selected base slot would push the static variables past the end of storage,
Expand Down
80 changes: 67 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,71 @@ 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 is 'address' instead)";
else if (auto const* fixedBytesType = dynamic_cast<FixedBytesType const*>(baseSlotExpressionType))
errorMsg += fmt::format(
" (the type is 'bytes{}' instead)",
fixedBytesType->numBytes()
)
;
Comment on lines +119 to +120
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
)
;
);

Same below.

else if (auto const* userDefinedType = dynamic_cast<UserDefinedValueType const*>(baseSlotExpressionType))
errorMsg += fmt::format(
" (the type is '{}' instead)",
userDefinedType->canonicalName()
)
;
errorMsg += ".";

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 +180,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(
"Base slot expression of type '{}' is not convertible to 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 is 'address' instead).
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
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 is 'address' instead).
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 is 'bytes32' instead).
// TypeError 1763: (87-96): The base slot of the storage layout must evaluate to an integer (the type is 'bytes1' instead).
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.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I find this message a bit too vague and a bit too verbose. I'm pretty sure we'll never support division by 0 in the constant evaluator (or otherwise), but then, ConstantEvaluator returns a nullopt in cases where division by zero is detected, and just swallows the error message, so it's practically impossible to differentiate cases.

Eh, you know what, the message is good as as, ignore my rambling.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree. This should just say that you can't divide by zero at compilation time. We should adjust ConstantEvaluator to report with more granularity why it cannot be evaluated. This way we will be able to tell the user which specific element is not yet supported (type conversion, member access, non-const builtin, etc.)

For the purpose of this PR it's good enough though. We should merge it and fix that separately.

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 is 'address' instead).
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): Base slot expression of type 'int256' is not convertible to 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 is 'bytes1' instead).
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 (the type is 'MyUint' instead).
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 (the type is 'MyUint' instead).
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.
Loading