ethereum / solidity

Solidity, the Smart Contract Programming Language
https://soliditylang.org
GNU General Public License v3.0
23.24k stars 5.75k forks source link

Generic way to access the underlying type of an UDVT #15318

Open aathan opened 2 months ago

aathan commented 2 months ago

Abstract

type().max min should work with user defined types that are mapped to scalar types

type MY_THING is uint32; MY_THING constant MY_THIN_FLAG=type(uint32).max;

is what's currently required because I can't write type(MY_THING).max.

Motivation

This is really unfortunate, as it destroys one of the main utilities of such a type alias. Now if I decide I want more bits for MY_THING I have to change the underlying type in multiple places = much larger probability of introducing a bug.

Specification

Allow type(user_defined_type) when user_defined_type maps to a scalar underlying type.

Backwards Compatibility

none

cameel commented 2 days ago

Thanks for the idea. There is something to it, but maybe using a different approach.

We don't think that type(MY_THIN_FLAG).max makes sense as equivalent to type(uint32).max, because the maximum (and minimum) is specific to the UDVT, not its representation. uint32 is just an implementation detail of the type and its maximum value may not even logically be a valid value of that UDVT. Having max accessible this way could be undesirable and misleading for some UDVTs.

Defining a constant actually seems like the right solution to your problem. This way you can define an arbitrary maximum/miniumum adequate for the type and you don't have to change it in multiple places. Though note that you need wrap() to properly define it:

MY_THING constant MY_THING_MAX = MY_THING.wrap(type(uint32).max);

What we'd be open to instead is letting the user refer to the underlying type in a generic way, e.g. with something like type(MY_THING).underlyingType or type(MY_THING).representationType. Then you'd be able to do this:

MY_THING.wrap(type(type(MY_THING).underlyingType).max);

Though TBH that's long enough that I'd want to hide it behind a constant anyway :)

In any case, this is very low priority until we have the new system with generics, where such a type could have more uses. Especially given that a constant seems like a pretty adequate workaround.