Skip to content

Commit 29cc7aa

Browse files
authored
Merge pull request #11847 from ethereum/enum-min-max-11469
Add type().min/max for enums
2 parents f4effe9 + 2b28f87 commit 29cc7aa

File tree

17 files changed

+85
-25
lines changed

17 files changed

+85
-25
lines changed

Changelog.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
Language Features:
44
* Inheritance: A function that overrides only a single interface function does not require the ``override`` specifier.
5+
* Type System: Support ``type().min`` and ``type().max`` for enums.
56

67

78
Compiler Features:

docs/types/value-types.rst

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -587,11 +587,14 @@ Enums cannot have more than 256 members.
587587
The data representation is the same as for enums in C: The options are represented by
588588
subsequent unsigned integer values starting from ``0``.
589589

590+
Using ``type(NameOfEnum).min`` and ``type(NameOfEnum).max`` you can get the
591+
smallest and respectively largest value of the given enum.
592+
590593

591594
.. code-block:: solidity
592595
593596
// SPDX-License-Identifier: GPL-3.0
594-
pragma solidity >=0.4.16 <0.9.0;
597+
pragma solidity ^0.8.8;
595598
596599
contract test {
597600
enum ActionChoices { GoLeft, GoRight, GoStraight, SitStill }
@@ -612,6 +615,14 @@ subsequent unsigned integer values starting from ``0``.
612615
function getDefaultChoice() public pure returns (uint) {
613616
return uint(defaultChoice);
614617
}
618+
619+
function getLargestValue() public pure returns (ActionChoices) {
620+
return type(ActionChoices).max;
621+
}
622+
623+
function getSmallestValue() public pure returns (ActionChoices) {
624+
return type(ActionChoices).min;
625+
}
615626
}
616627
617628
.. note::

libsolidity/analysis/TypeChecker.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,10 @@ TypePointers TypeChecker::typeCheckMetaTypeFunctionAndRetrieveReturnType(Functio
246246
Type::Category typeCategory = typeTypePtr->actualType()->category();
247247
if (auto const* contractType = dynamic_cast<ContractType const*>(typeTypePtr->actualType()))
248248
wrongType = contractType->isSuper();
249-
else if (typeCategory != Type::Category::Integer)
249+
else if (
250+
typeCategory != Type::Category::Integer &&
251+
typeCategory != Type::Category::Enum
252+
)
250253
wrongType = true;
251254
}
252255
else
@@ -257,7 +260,7 @@ TypePointers TypeChecker::typeCheckMetaTypeFunctionAndRetrieveReturnType(Functio
257260
4259_error,
258261
arguments.front()->location(),
259262
"Invalid type for argument in the function call. "
260-
"A contract type or an integer type is required, but " +
263+
"An enum type, contract type or an integer type is required, but " +
261264
type(*arguments.front())->toString(true) + " provided."
262265
);
263266

libsolidity/ast/TypeProvider.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -566,9 +566,10 @@ MagicType const* TypeProvider::meta(Type const* _type)
566566
solAssert(
567567
_type && (
568568
_type->category() == Type::Category::Contract ||
569-
_type->category() == Type::Category::Integer
569+
_type->category() == Type::Category::Integer ||
570+
_type->category() == Type::Category::Enum
570571
),
571-
"Only contracts or integer types supported for now."
572+
"Only enum, contracts or integer types supported for now."
572573
);
573574
return createAndGet<MagicType>(_type);
574575
}

libsolidity/ast/Types.cpp

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3961,9 +3961,10 @@ MemberList::MemberMap MagicType::nativeMembers(ASTNode const*) const
39613961
solAssert(
39623962
m_typeArgument && (
39633963
m_typeArgument->category() == Type::Category::Contract ||
3964-
m_typeArgument->category() == Type::Category::Integer
3964+
m_typeArgument->category() == Type::Category::Integer ||
3965+
m_typeArgument->category() == Type::Category::Enum
39653966
),
3966-
"Only contracts or integer types supported for now"
3967+
"Only enums, contracts or integer types supported for now"
39673968
);
39683969

39693970
if (m_typeArgument->category() == Type::Category::Contract)
@@ -3989,6 +3990,14 @@ MemberList::MemberMap MagicType::nativeMembers(ASTNode const*) const
39893990
{"max", integerTypePointer},
39903991
});
39913992
}
3993+
else if (m_typeArgument->category() == Type::Category::Enum)
3994+
{
3995+
EnumType const* enumTypePointer = dynamic_cast<EnumType const*>(m_typeArgument);
3996+
return MemberList::MemberMap({
3997+
{"min", enumTypePointer},
3998+
{"max", enumTypePointer},
3999+
});
4000+
}
39924001
}
39934002
}
39944003
solAssert(false, "Unknown kind of magic.");

libsolidity/ast/Types.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1071,6 +1071,12 @@ class EnumType: public Type
10711071
/// @returns the value that the string has in the Enum
10721072
unsigned int memberValue(ASTString const& _member) const;
10731073
size_t numberOfMembers() const;
1074+
unsigned int minValue() const { return 0; }
1075+
unsigned int maxValue() const
1076+
{
1077+
solAssert(numberOfMembers() <= 256, "");
1078+
return static_cast<unsigned int>(numberOfMembers()) - 1;
1079+
}
10741080

10751081
private:
10761082
EnumDefinition const& m_enum;

libsolidity/codegen/ExpressionCompiler.cpp

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1784,12 +1784,13 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
17841784
else if (member == "min" || member == "max")
17851785
{
17861786
MagicType const* arg = dynamic_cast<MagicType const*>(_memberAccess.expression().annotation().type);
1787-
IntegerType const* integerType = dynamic_cast<IntegerType const*>(arg->typeArgument());
1788-
1789-
if (member == "min")
1790-
m_context << integerType->min();
1787+
if (IntegerType const* integerType = dynamic_cast<IntegerType const*>(arg->typeArgument()))
1788+
m_context << (member == "min" ? integerType->min() : integerType->max());
1789+
else if (EnumType const* enumType = dynamic_cast<EnumType const*>(arg->typeArgument()))
1790+
m_context << (member == "min" ? enumType->minValue() : enumType->maxValue());
17911791
else
1792-
m_context << integerType->max();
1792+
solAssert(false, "min/max not available for the given type.");
1793+
17931794
}
17941795
else if ((set<string>{"encode", "encodePacked", "encodeWithSelector", "encodeWithSignature", "decode"}).count(member))
17951796
{

libsolidity/codegen/ir/IRGeneratorForStatements.cpp

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1785,12 +1785,26 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess)
17851785
else if (member == "min" || member == "max")
17861786
{
17871787
MagicType const* arg = dynamic_cast<MagicType const*>(_memberAccess.expression().annotation().type);
1788-
IntegerType const* integerType = dynamic_cast<IntegerType const*>(arg->typeArgument());
17891788

1790-
if (member == "min")
1791-
define(_memberAccess) << formatNumber(integerType->min()) << "\n";
1789+
string requestedValue;
1790+
if (IntegerType const* integerType = dynamic_cast<IntegerType const*>(arg->typeArgument()))
1791+
{
1792+
if (member == "min")
1793+
requestedValue = formatNumber(integerType->min());
1794+
else
1795+
requestedValue = formatNumber(integerType->max());
1796+
}
1797+
else if (EnumType const* enumType = dynamic_cast<EnumType const*>(arg->typeArgument()))
1798+
{
1799+
if (member == "min")
1800+
requestedValue = to_string(enumType->minValue());
1801+
else
1802+
requestedValue = to_string(enumType->maxValue());
1803+
}
17921804
else
1793-
define(_memberAccess) << formatNumber(integerType->max()) << "\n";
1805+
solAssert(false, "min/max requested on unexpected type.");
1806+
1807+
define(_memberAccess) << requestedValue << "\n";
17941808
}
17951809
else if (set<string>{"encode", "encodePacked", "encodeWithSelector", "encodeWithSignature", "decode"}.count(member))
17961810
{

libsolidity/formal/SMTEncoder.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1284,8 +1284,10 @@ bool SMTEncoder::visit(MemberAccess const& _memberAccess)
12841284
auto const& memberName = _memberAccess.memberName();
12851285
if (memberName == "min" || memberName == "max")
12861286
{
1287-
IntegerType const& integerType = dynamic_cast<IntegerType const&>(*magicType->typeArgument());
1288-
defineExpr(_memberAccess, memberName == "min" ? integerType.minValue() : integerType.maxValue());
1287+
if (IntegerType const* integerType = dynamic_cast<IntegerType const*>(magicType->typeArgument()))
1288+
defineExpr(_memberAccess, memberName == "min" ? integerType->minValue() : integerType->maxValue());
1289+
else if (EnumType const* enumType = dynamic_cast<EnumType const*>(magicType->typeArgument()))
1290+
defineExpr(_memberAccess, memberName == "min" ? enumType->minValue() : enumType->maxValue());
12891291
}
12901292
else if (memberName == "interfaceId")
12911293
{
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
contract test {
2+
enum MinMax { A, B, C, D }
3+
4+
function min() public returns(uint) { return uint(type(MinMax).min); }
5+
function max() public returns(uint) { return uint(type(MinMax).max); }
6+
}
7+
8+
// ====
9+
// compileViaYul: also
10+
// ----
11+
// min() -> 0
12+
// max() -> 3

0 commit comments

Comments
 (0)