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:
|
||||
* 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:
|
||||
|
@ -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
|
||||
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
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.4.16 <0.9.0;
|
||||
pragma solidity ^0.8.8;
|
||||
|
||||
contract test {
|
||||
enum ActionChoices { GoLeft, GoRight, GoStraight, SitStill }
|
||||
@ -612,6 +615,14 @@ subsequent unsigned integer values starting from ``0``.
|
||||
function getDefaultChoice() public pure returns (uint) {
|
||||
return uint(defaultChoice);
|
||||
}
|
||||
|
||||
function getLargestValue() public pure returns (ActionChoices) {
|
||||
return type(ActionChoices).max;
|
||||
}
|
||||
|
||||
function getSmallestValue() public pure returns (ActionChoices) {
|
||||
return type(ActionChoices).min;
|
||||
}
|
||||
}
|
||||
|
||||
.. note::
|
||||
|
@ -246,7 +246,10 @@ TypePointers TypeChecker::typeCheckMetaTypeFunctionAndRetrieveReturnType(Functio
|
||||
Type::Category typeCategory = typeTypePtr->actualType()->category();
|
||||
if (auto const* contractType = dynamic_cast<ContractType const*>(typeTypePtr->actualType()))
|
||||
wrongType = contractType->isSuper();
|
||||
else if (typeCategory != Type::Category::Integer)
|
||||
else if (
|
||||
typeCategory != Type::Category::Integer &&
|
||||
typeCategory != Type::Category::Enum
|
||||
)
|
||||
wrongType = true;
|
||||
}
|
||||
else
|
||||
@ -257,7 +260,7 @@ TypePointers TypeChecker::typeCheckMetaTypeFunctionAndRetrieveReturnType(Functio
|
||||
4259_error,
|
||||
arguments.front()->location(),
|
||||
"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."
|
||||
);
|
||||
|
||||
|
@ -566,9 +566,10 @@ MagicType const* TypeProvider::meta(Type const* _type)
|
||||
solAssert(
|
||||
_type && (
|
||||
_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);
|
||||
}
|
||||
|
@ -3961,9 +3961,10 @@ MemberList::MemberMap MagicType::nativeMembers(ASTNode const*) const
|
||||
solAssert(
|
||||
m_typeArgument && (
|
||||
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)
|
||||
@ -3989,6 +3990,14 @@ MemberList::MemberMap MagicType::nativeMembers(ASTNode const*) const
|
||||
{"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.");
|
||||
|
@ -1071,6 +1071,12 @@ public:
|
||||
/// @returns the value that the string has in the Enum
|
||||
unsigned int memberValue(ASTString const& _member) 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:
|
||||
EnumDefinition const& m_enum;
|
||||
|
@ -1784,12 +1784,13 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
|
||||
else if (member == "min" || member == "max")
|
||||
{
|
||||
MagicType const* arg = dynamic_cast<MagicType const*>(_memberAccess.expression().annotation().type);
|
||||
IntegerType const* integerType = dynamic_cast<IntegerType const*>(arg->typeArgument());
|
||||
|
||||
if (member == "min")
|
||||
m_context << integerType->min();
|
||||
if (IntegerType const* integerType = dynamic_cast<IntegerType const*>(arg->typeArgument()))
|
||||
m_context << (member == "min" ? integerType->min() : integerType->max());
|
||||
else if (EnumType const* enumType = dynamic_cast<EnumType const*>(arg->typeArgument()))
|
||||
m_context << (member == "min" ? enumType->minValue() : enumType->maxValue());
|
||||
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))
|
||||
{
|
||||
|
@ -1785,12 +1785,26 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess)
|
||||
else if (member == "min" || member == "max")
|
||||
{
|
||||
MagicType const* arg = dynamic_cast<MagicType const*>(_memberAccess.expression().annotation().type);
|
||||
IntegerType const* integerType = dynamic_cast<IntegerType const*>(arg->typeArgument());
|
||||
|
||||
if (member == "min")
|
||||
define(_memberAccess) << formatNumber(integerType->min()) << "\n";
|
||||
string requestedValue;
|
||||
if (IntegerType const* integerType = dynamic_cast<IntegerType const*>(arg->typeArgument()))
|
||||
{
|
||||
if (member == "min")
|
||||
requestedValue = formatNumber(integerType->min());
|
||||
else
|
||||
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
|
||||
define(_memberAccess) << formatNumber(integerType->max()) << "\n";
|
||||
solAssert(false, "min/max requested on unexpected type.");
|
||||
|
||||
define(_memberAccess) << requestedValue << "\n";
|
||||
}
|
||||
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();
|
||||
if (memberName == "min" || memberName == "max")
|
||||
{
|
||||
IntegerType const& integerType = dynamic_cast<IntegerType const&>(*magicType->typeArgument());
|
||||
defineExpr(_memberAccess, memberName == "min" ? integerType.minValue() : integerType.maxValue());
|
||||
if (IntegerType const* integerType = dynamic_cast<IntegerType const*>(magicType->typeArgument()))
|
||||
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")
|
||||
{
|
||||
|
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 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