[feature preview] User-defined operators

I’m sorry, but unless I’m missing something here, you’re just arguing semantics,

It’s not just semantics unless you’re arguing that there’s absolutely no difference between UDVTs and built-in value types. They’re different and the latter had operators while the former did not. The feature is primiarily meant to fill that gap. I’m sorry if it’s disappointing.

How exactly is that useful, when I can simply apply arithmetic operators on the (original) built-in type?

I gave two examples in my previous comment. It brings UDVTs closer in usability to built-in value types. No more, no less. I’m not saying that it couldn’t be more useful, just that what the feature provides now already has practical applications.

1 Like

Would be nice if this would work with mixed types. Right now this couldn’t even be used with the example over at Types — Solidity 0.8.17 documentation right?

Yeah, it couldn’t be used as is, though in this particular example you can easily deal with it by having mul() take UFixed256x18 as the second argument as well.

Using operators on mixed types will be supported in one way or another eventually, but having to declare separate operators for all type combinations is not the only way to do it and we could not yet agree on which way is the best. For now we decided to go forward with a minimal version of the feature and get some feedback from the community.

One big problem with mixed types is associativity: Say, you define add(UncheckedInt, UncheckedInt) and add(UncheckedInt, int) as +. Should UncheckedInt.wrap(1) + UncheckedInt.wrap(2) + 3 be parsed as (a + b) + c or a + (b + c)? We do not see a nice way to specify that explicitly and just assuming that it’s always the same as the current operators is not good enough.

Another thing is interaction with generics. Having operators with generic types and only restricting them via traits might be the way we decide to go and adding mixed concrete types to the mix would complicate this.

Finally, implicit conversions between types also complicate things, though for now we avoided that by sticking only to UDVTs, which can have have no implicit conversions for now.

See the comment thread #9211 starting with this comment and also #11969 if you want to read some discussion on that topic with things we considered.

1 Like

I have just posted a new preview binary. See updated description for links and a short summary of changes compared to the previous preview.

Here’s also some more context on changes.

Based on the feedback we got, we decided to make the initial implementation more restrictive. The biggest change is that now operators can only be defined using free functions. Most importantly, this prevents them from being external - our biggest concern was that with public library functions it’s not always clear to users whether they will end up being called externally or internally. It’s actually not all that clear with attached functions either (#13908). Disallowing it makes implementation simpler and we think that external operators would not be all that useful in practice (but we’re open to hearing about potential use cases).

Operators also must be global now (i.e. defined with using {...} for ... global). We were going to recommend this as a best practice anyway. This will prevent types from having different sets of operators in different contexts. Since global using for is only allowed in the source unit that defines the type, this also means that their definitions must be located there. This change also makes it impossible to have operators on types defined locally in contracts, libraries and interfaces, because they’re not accessible in the file scope and using for global is not allowed in other scopes.

As for <<, >>, ** and !, we still need more time to decide about their types so we removed them from the initial implementation. We’ll also consider adding increment/decrement and assignment operators in future releases. We might also introduce something like C++ spaceship operator to reduce boilerplate when defining comparison operators.

Operators on mixed types and on reference types will eventually come, but that will only happen after we’re done with the changes to the type system that are on our current roadmap. Doing them before would only result in something that would constrain our design choices and would have to be thrown out in a breaking release, annoying people who decided to use them.

Finally, note that SMTChecker does not fully support user-defined operators yet. It will process files containing them but not detect them as function calls. This will be implemented in one of the upcoming releases.

1 Like