Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add type().length for enums #11469

Closed
Amxx opened this issue Jun 1, 2021 · 10 comments · Fixed by #11847
Closed

Add type().length for enums #11469

Amxx opened this issue Jun 1, 2021 · 10 comments · Fixed by #11847
Assignees
Labels
language design :rage4: Any changes to the language, e.g. new features

Comments

@Amxx
Copy link

Amxx commented Jun 1, 2021

Abstract

Add a mechanism to get the number of entries in an Enum using the enum's name

contract Test {
    enum MyEnum { A, B, C, D }
    
    function length() external view returns (uint8) {
        return type(MyEnum).length; // Should return 4
    } 

Motivation

There is already a mechanism to get type().min and type().max for integers types. In some cases it might be necessary to check if a uint does match an enum without reverting (casting will revert of not within limits)

Specification

type(MyEnum).length should returns a uin8

Alternative

An alternative would be to have type(MyEnum).min and type(MyEnum).max supported.

  • .min would always return 0
  • .max would return the length

Backwards Compatibility

N/A

@cameel cameel added feature language design :rage4: Any changes to the language, e.g. new features labels Jun 1, 2021
@axic
Copy link
Member

axic commented Jun 1, 2021

Why .length and not .min/.max (even if min currently is a constant 0)?

@Amxx
Copy link
Author

Amxx commented Jun 1, 2021

Min / Max would work just fine I guess.

@chriseth
Copy link
Contributor

chriseth commented Jun 2, 2021

@Amxx could you please explain a bit more how you would use this feature and why the current set of features is not sufficient?

@hrkrshnn
Copy link
Member

hrkrshnn commented Jun 3, 2021

If it's min and max, I think it should be returning a value in the Enum instead of uint8. In the example above, it would be MyEnum.A and MyEnum.D respectively.

@chriseth
Copy link
Contributor

chriseth commented Jun 3, 2021

Actually I just checked and the enum values are indeed <-comparable, so min / max would make sense, and I agree with @hrkrshnn that the values should be of the enum type.

@axic
Copy link
Member

axic commented Jun 3, 2021

We did discuss during the meeting, that potentially a more generic solution following #11284 is to have something akin to Rust's "try_convert". That would return a boolean in case of failure, unlike the compiler generated error we have on out-of-bounds int to enum casting today.

@Amxx
Copy link
Author

Amxx commented Jun 3, 2021

@Amxx could you please explain a bit more how you would use this feature and why the current set of features is not sufficient?

I don't like justifying with my particular usecase, because I always have the feeling the my usecase is just one of many cases, and not necessarily the best one. Also, this doesn't allow to do anything that was impossible before. It just helps (just like type(uint256).max doesn't really provide that was not possible to express before).

That being said, here is an idea of a library. It defines a structure that is holding arbitrary values, but has a few "reserved values" at the beginning of the rang. In my case the arbitrary values are timestamp, so no realistic risk of clash

library MyLib {
    enum ReservedValues {
        Unset,    // 0
        Finished, // 1
        Canceled, // 2
        length
    }

    struct MyStruct {
        uint256 _value;
    }

    function getValue(MyStruct memory s) internal pure returns (uint256) {
        return s._value;
    }

    // ... lot of stuff ...

    function isUnset(MyStruct memory s) internal pure returns (bool) {
        return getValue(s) == uint256(ReservedTimestamps.Unset);
    }

    function isFinished(MyStruct memory s) internal pure returns (bool) {
        return getValue(s) == uint256(ReservedTimestamps.Finished);
    }
    
    function isCanceled(MyStruct memory s) internal pure returns (bool) {
        return getValue(s) == uint256(ReservedTimestamps.Canceled);
    }

    function isOther(MyStruct memory s) internal pure returns (bool) {
        return getValue(s) >= uint256(ReservedTimestamps.length);
    }

    // ... lot of stuff ...
}

As you can see, I have a mechanism to check if the value is reserved or not, by comparing it to the length. To get the length, my trick is to add a length element a the end of the enum. This works great but I'm worried that someone might fork it, add elements AFTER the length entry in the enum, and break everything. This is why I imagined that a length / max / last mechanism would be safer.

@chriseth
Copy link
Contributor

chriseth commented Jun 9, 2021

@axic any results on finding a more generic solution? We could just add .min / .max for now, too.

@chriseth
Copy link
Contributor

chriseth commented Jun 9, 2021

We decided in the call that we will implement this a min/max.

@axic
Copy link
Member

axic commented Jun 9, 2021

Apart from #11469 (comment), no. At the time we handle conversion changes, that will be a larger breaking release, and it would be possible to drop .min/.max if it does not seem appropriate anymore. Even if it is not dropped, the conversions may be a better way to handle this in user code, but users are free to choose.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
language design :rage4: Any changes to the language, e.g. new features
Projects
None yet
Development

Successfully merging a pull request may close this issue.

6 participants