mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #11847 from ethereum/enum-min-max-11469
Add type().min/max for enums
This commit is contained in:
commit
29cc7aa951
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
Language Features:
|
Language Features:
|
||||||
* Inheritance: A function that overrides only a single interface function does not require the ``override`` specifier.
|
* Inheritance: A function that overrides only a single interface function does not require the ``override`` specifier.
|
||||||
|
* Type System: Support ``type().min`` and ``type().max`` for enums.
|
||||||
|
|
||||||
|
|
||||||
Compiler Features:
|
Compiler Features:
|
||||||
|
@ -587,11 +587,14 @@ Enums cannot have more than 256 members.
|
|||||||
The data representation is the same as for enums in C: The options are represented by
|
The data representation is the same as for enums in C: The options are represented by
|
||||||
subsequent unsigned integer values starting from ``0``.
|
subsequent unsigned integer values starting from ``0``.
|
||||||
|
|
||||||
|
Using ``type(NameOfEnum).min`` and ``type(NameOfEnum).max`` you can get the
|
||||||
|
smallest and respectively largest value of the given enum.
|
||||||
|
|
||||||
|
|
||||||
.. code-block:: solidity
|
.. code-block:: solidity
|
||||||
|
|
||||||
// SPDX-License-Identifier: GPL-3.0
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.4.16 <0.9.0;
|
pragma solidity ^0.8.8;
|
||||||
|
|
||||||
contract test {
|
contract test {
|
||||||
enum ActionChoices { GoLeft, GoRight, GoStraight, SitStill }
|
enum ActionChoices { GoLeft, GoRight, GoStraight, SitStill }
|
||||||
@ -612,6 +615,14 @@ subsequent unsigned integer values starting from ``0``.
|
|||||||
function getDefaultChoice() public pure returns (uint) {
|
function getDefaultChoice() public pure returns (uint) {
|
||||||
return uint(defaultChoice);
|
return uint(defaultChoice);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getLargestValue() public pure returns (ActionChoices) {
|
||||||
|
return type(ActionChoices).max;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSmallestValue() public pure returns (ActionChoices) {
|
||||||
|
return type(ActionChoices).min;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
@ -246,7 +246,10 @@ TypePointers TypeChecker::typeCheckMetaTypeFunctionAndRetrieveReturnType(Functio
|
|||||||
Type::Category typeCategory = typeTypePtr->actualType()->category();
|
Type::Category typeCategory = typeTypePtr->actualType()->category();
|
||||||
if (auto const* contractType = dynamic_cast<ContractType const*>(typeTypePtr->actualType()))
|
if (auto const* contractType = dynamic_cast<ContractType const*>(typeTypePtr->actualType()))
|
||||||
wrongType = contractType->isSuper();
|
wrongType = contractType->isSuper();
|
||||||
else if (typeCategory != Type::Category::Integer)
|
else if (
|
||||||
|
typeCategory != Type::Category::Integer &&
|
||||||
|
typeCategory != Type::Category::Enum
|
||||||
|
)
|
||||||
wrongType = true;
|
wrongType = true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -257,7 +260,7 @@ TypePointers TypeChecker::typeCheckMetaTypeFunctionAndRetrieveReturnType(Functio
|
|||||||
4259_error,
|
4259_error,
|
||||||
arguments.front()->location(),
|
arguments.front()->location(),
|
||||||
"Invalid type for argument in the function call. "
|
"Invalid type for argument in the function call. "
|
||||||
"A contract type or an integer type is required, but " +
|
"An enum type, contract type or an integer type is required, but " +
|
||||||
type(*arguments.front())->toString(true) + " provided."
|
type(*arguments.front())->toString(true) + " provided."
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -566,9 +566,10 @@ MagicType const* TypeProvider::meta(Type const* _type)
|
|||||||
solAssert(
|
solAssert(
|
||||||
_type && (
|
_type && (
|
||||||
_type->category() == Type::Category::Contract ||
|
_type->category() == Type::Category::Contract ||
|
||||||
_type->category() == Type::Category::Integer
|
_type->category() == Type::Category::Integer ||
|
||||||
|
_type->category() == Type::Category::Enum
|
||||||
),
|
),
|
||||||
"Only contracts or integer types supported for now."
|
"Only enum, contracts or integer types supported for now."
|
||||||
);
|
);
|
||||||
return createAndGet<MagicType>(_type);
|
return createAndGet<MagicType>(_type);
|
||||||
}
|
}
|
||||||
|
@ -3961,9 +3961,10 @@ MemberList::MemberMap MagicType::nativeMembers(ASTNode const*) const
|
|||||||
solAssert(
|
solAssert(
|
||||||
m_typeArgument && (
|
m_typeArgument && (
|
||||||
m_typeArgument->category() == Type::Category::Contract ||
|
m_typeArgument->category() == Type::Category::Contract ||
|
||||||
m_typeArgument->category() == Type::Category::Integer
|
m_typeArgument->category() == Type::Category::Integer ||
|
||||||
|
m_typeArgument->category() == Type::Category::Enum
|
||||||
),
|
),
|
||||||
"Only contracts or integer types supported for now"
|
"Only enums, contracts or integer types supported for now"
|
||||||
);
|
);
|
||||||
|
|
||||||
if (m_typeArgument->category() == Type::Category::Contract)
|
if (m_typeArgument->category() == Type::Category::Contract)
|
||||||
@ -3989,6 +3990,14 @@ MemberList::MemberMap MagicType::nativeMembers(ASTNode const*) const
|
|||||||
{"max", integerTypePointer},
|
{"max", integerTypePointer},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
else if (m_typeArgument->category() == Type::Category::Enum)
|
||||||
|
{
|
||||||
|
EnumType const* enumTypePointer = dynamic_cast<EnumType const*>(m_typeArgument);
|
||||||
|
return MemberList::MemberMap({
|
||||||
|
{"min", enumTypePointer},
|
||||||
|
{"max", enumTypePointer},
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
solAssert(false, "Unknown kind of magic.");
|
solAssert(false, "Unknown kind of magic.");
|
||||||
|
@ -1071,6 +1071,12 @@ public:
|
|||||||
/// @returns the value that the string has in the Enum
|
/// @returns the value that the string has in the Enum
|
||||||
unsigned int memberValue(ASTString const& _member) const;
|
unsigned int memberValue(ASTString const& _member) const;
|
||||||
size_t numberOfMembers() const;
|
size_t numberOfMembers() const;
|
||||||
|
unsigned int minValue() const { return 0; }
|
||||||
|
unsigned int maxValue() const
|
||||||
|
{
|
||||||
|
solAssert(numberOfMembers() <= 256, "");
|
||||||
|
return static_cast<unsigned int>(numberOfMembers()) - 1;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
EnumDefinition const& m_enum;
|
EnumDefinition const& m_enum;
|
||||||
|
@ -1784,12 +1784,13 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
|
|||||||
else if (member == "min" || member == "max")
|
else if (member == "min" || member == "max")
|
||||||
{
|
{
|
||||||
MagicType const* arg = dynamic_cast<MagicType const*>(_memberAccess.expression().annotation().type);
|
MagicType const* arg = dynamic_cast<MagicType const*>(_memberAccess.expression().annotation().type);
|
||||||
IntegerType const* integerType = dynamic_cast<IntegerType const*>(arg->typeArgument());
|
if (IntegerType const* integerType = dynamic_cast<IntegerType const*>(arg->typeArgument()))
|
||||||
|
m_context << (member == "min" ? integerType->min() : integerType->max());
|
||||||
if (member == "min")
|
else if (EnumType const* enumType = dynamic_cast<EnumType const*>(arg->typeArgument()))
|
||||||
m_context << integerType->min();
|
m_context << (member == "min" ? enumType->minValue() : enumType->maxValue());
|
||||||
else
|
else
|
||||||
m_context << integerType->max();
|
solAssert(false, "min/max not available for the given type.");
|
||||||
|
|
||||||
}
|
}
|
||||||
else if ((set<string>{"encode", "encodePacked", "encodeWithSelector", "encodeWithSignature", "decode"}).count(member))
|
else if ((set<string>{"encode", "encodePacked", "encodeWithSelector", "encodeWithSignature", "decode"}).count(member))
|
||||||
{
|
{
|
||||||
|
@ -1785,12 +1785,26 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess)
|
|||||||
else if (member == "min" || member == "max")
|
else if (member == "min" || member == "max")
|
||||||
{
|
{
|
||||||
MagicType const* arg = dynamic_cast<MagicType const*>(_memberAccess.expression().annotation().type);
|
MagicType const* arg = dynamic_cast<MagicType const*>(_memberAccess.expression().annotation().type);
|
||||||
IntegerType const* integerType = dynamic_cast<IntegerType const*>(arg->typeArgument());
|
|
||||||
|
|
||||||
|
string requestedValue;
|
||||||
|
if (IntegerType const* integerType = dynamic_cast<IntegerType const*>(arg->typeArgument()))
|
||||||
|
{
|
||||||
if (member == "min")
|
if (member == "min")
|
||||||
define(_memberAccess) << formatNumber(integerType->min()) << "\n";
|
requestedValue = formatNumber(integerType->min());
|
||||||
else
|
else
|
||||||
define(_memberAccess) << formatNumber(integerType->max()) << "\n";
|
requestedValue = formatNumber(integerType->max());
|
||||||
|
}
|
||||||
|
else if (EnumType const* enumType = dynamic_cast<EnumType const*>(arg->typeArgument()))
|
||||||
|
{
|
||||||
|
if (member == "min")
|
||||||
|
requestedValue = to_string(enumType->minValue());
|
||||||
|
else
|
||||||
|
requestedValue = to_string(enumType->maxValue());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
solAssert(false, "min/max requested on unexpected type.");
|
||||||
|
|
||||||
|
define(_memberAccess) << requestedValue << "\n";
|
||||||
}
|
}
|
||||||
else if (set<string>{"encode", "encodePacked", "encodeWithSelector", "encodeWithSignature", "decode"}.count(member))
|
else if (set<string>{"encode", "encodePacked", "encodeWithSelector", "encodeWithSignature", "decode"}.count(member))
|
||||||
{
|
{
|
||||||
|
@ -1284,8 +1284,10 @@ bool SMTEncoder::visit(MemberAccess const& _memberAccess)
|
|||||||
auto const& memberName = _memberAccess.memberName();
|
auto const& memberName = _memberAccess.memberName();
|
||||||
if (memberName == "min" || memberName == "max")
|
if (memberName == "min" || memberName == "max")
|
||||||
{
|
{
|
||||||
IntegerType const& integerType = dynamic_cast<IntegerType const&>(*magicType->typeArgument());
|
if (IntegerType const* integerType = dynamic_cast<IntegerType const*>(magicType->typeArgument()))
|
||||||
defineExpr(_memberAccess, memberName == "min" ? integerType.minValue() : integerType.maxValue());
|
defineExpr(_memberAccess, memberName == "min" ? integerType->minValue() : integerType->maxValue());
|
||||||
|
else if (EnumType const* enumType = dynamic_cast<EnumType const*>(magicType->typeArgument()))
|
||||||
|
defineExpr(_memberAccess, memberName == "min" ? enumType->minValue() : enumType->maxValue());
|
||||||
}
|
}
|
||||||
else if (memberName == "interfaceId")
|
else if (memberName == "interfaceId")
|
||||||
{
|
{
|
||||||
|
12
test/libsolidity/semanticTests/enums/minmax.sol
Normal file
12
test/libsolidity/semanticTests/enums/minmax.sol
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
contract test {
|
||||||
|
enum MinMax { A, B, C, D }
|
||||||
|
|
||||||
|
function min() public returns(uint) { return uint(type(MinMax).min); }
|
||||||
|
function max() public returns(uint) { return uint(type(MinMax).max); }
|
||||||
|
}
|
||||||
|
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
|
// ----
|
||||||
|
// min() -> 0
|
||||||
|
// max() -> 3
|
@ -5,4 +5,4 @@ contract C {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// ----
|
// ----
|
||||||
// TypeError 4259: (93-98): Invalid type for argument in the function call. A contract type or an integer type is required, but type(bytes) provided.
|
// TypeError 4259: (93-98): Invalid type for argument in the function call. An enum type, contract type or an integer type is required, but type(bytes) provided.
|
||||||
|
@ -10,4 +10,4 @@ contract SuperTest is Other {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// ----
|
// ----
|
||||||
// TypeError 4259: (177-182): Invalid type for argument in the function call. A contract type or an integer type is required, but type(contract super SuperTest) provided.
|
// TypeError 4259: (177-182): Invalid type for argument in the function call. An enum type, contract type or an integer type is required, but type(contract super SuperTest) provided.
|
||||||
|
@ -14,4 +14,4 @@ abstract contract Test is ERC165 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// ----
|
// ----
|
||||||
// TypeError 4259: (592-597): Invalid type for argument in the function call. A contract type or an integer type is required, but type(contract super Test) provided.
|
// TypeError 4259: (592-597): Invalid type for argument in the function call. An enum type, contract type or an integer type is required, but type(contract super Test) provided.
|
||||||
|
@ -50,4 +50,4 @@ contract D is B, C {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// ----
|
// ----
|
||||||
// TypeError 4259: (426-431): Invalid type for argument in the function call. A contract type or an integer type is required, but type(contract super B) provided.
|
// TypeError 4259: (426-431): Invalid type for argument in the function call. An enum type, contract type or an integer type is required, but type(contract super B) provided.
|
||||||
|
@ -4,4 +4,4 @@ contract Test {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// ----
|
// ----
|
||||||
// TypeError 4259: (65-75): Invalid type for argument in the function call. A contract type or an integer type is required, but type(contract Test) provided.
|
// TypeError 4259: (65-75): Invalid type for argument in the function call. An enum type, contract type or an integer type is required, but type(contract Test) provided.
|
||||||
|
@ -6,4 +6,4 @@ contract Test {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// ----
|
// ----
|
||||||
// TypeError 4259: (154-155): Invalid type for argument in the function call. A contract type or an integer type is required, but type(struct Test.S) provided.
|
// TypeError 4259: (154-155): Invalid type for argument in the function call. An enum type, contract type or an integer type is required, but type(struct Test.S) provided.
|
||||||
|
@ -14,4 +14,4 @@ contract C {
|
|||||||
// ----
|
// ----
|
||||||
// TypeError 5347: (72-76): Try can only be used with external function calls and contract creation calls.
|
// TypeError 5347: (72-76): Try can only be used with external function calls and contract creation calls.
|
||||||
// TypeError 2536: (119-128): Try can only be used with external function calls and contract creation calls.
|
// TypeError 2536: (119-128): Try can only be used with external function calls and contract creation calls.
|
||||||
// TypeError 4259: (176-183): Invalid type for argument in the function call. A contract type or an integer type is required, but type(address) provided.
|
// TypeError 4259: (176-183): Invalid type for argument in the function call. An enum type, contract type or an integer type is required, but type(address) provided.
|
||||||
|
Loading…
Reference in New Issue
Block a user