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

Enum Type Inference #230

Merged
merged 22 commits into from
Nov 18, 2022
Merged

Enum Type Inference #230

merged 22 commits into from
Nov 18, 2022

Conversation

ichordev
Copy link
Contributor

No description provided.

DIPs/1NNN-AP.md Outdated Show resolved Hide resolved
DIPs/1NNN-AP.md Outdated Show resolved Hide resolved
DIPs/1NNN-AP.md Outdated Show resolved Hide resolved
DIPs/1NNN-AP.md Outdated Show resolved Hide resolved
Co-authored-by: Nick Treleaven <ntrel002@gmail.com>
DIPs/1NNN-AP.md Outdated Show resolved Hide resolved
DIPs/1NNN-AP.md Outdated
struct B{ A one, two; }

void main(){
A myA1 = $b; //myA1 = A.b
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should compound expressions work? E.g. A x = $a | $b;. Ditto for return $a | $b;.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should compound expressions work? E.g. A x = $a | $b;. Ditto for return $a | $b;.

I think I'm unfamiliar with this syntax, what does bitwise ORing two enum members result in?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The same thing as if you were bitwise ORing their base type.

Copy link
Contributor

@ntrel ntrel Aug 12, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This gives A:

pragma(msg, typeof(A.a | A.b));

(It would be better if that produced an integer, but it doesn't).

Copy link
Contributor Author

@ichordev ichordev Aug 12, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh dear... this is some... interesting baggage you two have uncovered that I would've never even thought about.

From my testing, I think it works like this:

enum A{ a,b,c,d }
enum B{ a,b,c,d }

A.a | A.b /*becomes*/ cast(int)A.a > cast(int)A.b ? A.a : A.b
2   | A.b /*becomes*/ 2 | cast(int)A.b
A.a | B.b /*becomes*/ cast(int)A.a > cast(int)B.b ? cast(int)A.a : cast(int)B.b

So, the answer to ntrel's question "Should compound expressions work?" is yes, since only ORing two enums of the same type produces an enum of that type, everything else produces an int.

I think this raises a better question though. Should this work? ;)

enum A{ a,b,c,d,e,f,g,h }

auto myA = cast(A)($b | 2 | $e); //oh no what is going on

In my opinion, no. I can't comprehend what in the world someone would be trying to achieve by doing this, haha!

P.S. I'll edit my examples to demonstrate ORing with ETI.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. Just to point out, enum ORing is typically used with non-overlapping enum values such as a=1, b=2, c=4, d=8, e=16 etc to pass multiple flags as a function argument. So a | c would be 5. Then the parameter received can be ANDed with c to see if c is set.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. Just to point out, enum ORing is typically used with non-overlapping enum values such as a=1, b=2, c=4, d=8, e=16 etc to pass multiple flags as a function argument. So a | c would be 5. Then the parameter received can be ANDed with c to see if c is set.

Saying that, I think a syntax like this would be very useful for C API wrappers. would Do you know if it would be feasible to make this work without breaking changes?

enum Flags{ a=1,b=2,c=4,d=8 }
void setFlags(int flags);
int flagVar;

void main(){
    setFlags(Flags.a | $b | $d);
    flagVar = Flags.d + $a + $a;
    flagVar = Flags.b & $c;
}

I'm picturing it having the same rules as the array literal syntax. (first—or maybe closest to the left?—enum type used for ETI, can be interchanged with integers, maybe floats)

@ntrel

This comment was marked as resolved.

@ichordev
Copy link
Contributor Author

I'd love to write a "grammatical changes" section, but I'm honestly not sure what D grammar is the equivalent of "EnumType.EnumMember"—an enum literal. Perhaps I'd need to add a new grammar label (not sure if this is the right term) for expressions?

@ntrel
Copy link
Contributor

ntrel commented Aug 15, 2022

I think just add $ Identifier under PrimaryExpression:
https://dlang.org/spec/expression.html#primary_expressions

DIPs/1NNN-AP.md Outdated Show resolved Hide resolved
Copy link
Contributor

@dukc dukc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In simple cases, this is just an excellent way to save on typing. More difficult question is, how far should inference of the enum type go? One case to consider in general is derived types, meaning can

enum C{ e,f,g,h }
enum D:C{ i=C.f, j=C.g }

...recognise members of each other with ETI?

If we were to go really ambitious with this, we could say that ETI should apply to any assignment, not just to assignment to enums. For example, we could do int x = $min, or Monotime time = $currTime. But this would be quite a mission creep so I won't say you should go there.

DIPs/1NNN-AP.md Show resolved Hide resolved
DIPs/1NNN-AP.md Outdated Show resolved Hide resolved
@ichordev
Copy link
Contributor Author

[...] how far should inference of the enum type go? One case to consider in general is derived types, meaning can

enum C{ e,f,g,h }
enum D:C{ i=C.f, j=C.g }

...recognise members of each other with ETI?

I don't see why that shouldn't work. I've tweaked the DIP's wording so that it doesn't sound like it's explicitly ruling this out, at least.

For instance, your example is a type delcaration; I didn't give any examples involving type delcarations, but your example follows the principle of all other cases where ETI should be allowed.

If we were to go really ambitious with this, we could say that ETI should apply to any assignment, not just to assignment to enums. For example, we could do int x = $min, or Monotime time = $currTime`. But this would be quite a mission creep so I won't say you should go there.

Don't worry, I would love to author future DIPs for this, but I think that ETI should come first. Think there's very little to disagree over about ETI. It's a simple, lovely little QoL feature that I sorely miss when using D over, say, Swift.

One step at a time is the way to go. If everyone can agree on a little change, then a bigger change in the same vein will have an easier time going through the review stages since it'll have fewer issues on its own. If I wrote a DIP for inference of EVERYTHING… well, I would be so overwhelmed with revising it that I would go insane!

@mdparker mdparker merged commit 967f4a8 into dlang:master Nov 18, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants