dao icon indicating copy to clipboard operation
dao copied to clipboard

Bitwise operations do not work on flag enum literals.

Open ShadauxCat opened this issue 10 years ago • 8 comments

enum MyEnum
{
   A;
   B;
   C;
}

var AB = MyEnum.A + MyEnum.B;
var BC = MyEnum.B + MyEnum.C;
var intersection = AB & BC;

This works correctly - intersection is equal to MyEnum.B.

enum MyEnum
{
   A;
   B;
   C;
}

var intersection = MyEnum.A & MyEnum.C;
>>> [[ERROR]] in file "code string+":
  At line 1 : Invalid statement --- " MyEnum ";
  At line 1 : Invalid expression --- " MyEnum. A & MyEnum. C; ";
  At line 1 : invalid constant expression;
  At line 1 : Constant evaluation aborted with exception(s) --- 
[[Error::Value]] --- Invalid value:
invalid operands
In code snippet:
>>    0 :  DATA        :     0 ,     0 ,     0 ;     0;   
Raised by:  (), at instruction 0 in line 0 in file "interactive codes";

This doesn't work. I would expect anything that can be done on a variable containing the enum would be possible on a literal of the enum.

ShadauxCat avatar Feb 22 '15 23:02 ShadauxCat

& is not a valid operation on enums, and var intersection = AB & BC; doesn't work for me (tried in the interactive mode). I don't think intersection have any real meaning and application in the context of enums.

Night-walker avatar Feb 23 '15 07:02 Night-walker

Sorry, but that's another case of "I haven't used it, so no one has."

An intersection on flags means "I have two objects and I want to get the list of flags that applies to both of them." There are use cases for that.

ShadauxCat avatar Feb 23 '15 12:02 ShadauxCat

Also, I tried var intersection = AB & BC; in interactive mode as well, and it worked fine. I'm using the latest code pulled from the git repository.

ShadauxCat avatar Feb 23 '15 12:02 ShadauxCat

Just to give a practical example of where flag (or set) intersection is extremely useful:

Suppose I'm writing the game The Sims. Now suppose in my AI routine, I have two Sims who have gotten together and now the routine determines they want to do something together (say, go on a date).

Now each Sim could have a list of things they're interested in. This might be stored as a set, but to save memory (on consoles, saving even a few bytes of memory is still important), they're stored as flags. (And this, by the way, is also an example of a case where more than 32 flags could very well be necessary. The Sims might have a LOT of possible things for a Sim to be interested in.)

So each Sim has a set of flags saying what they're interested in. One of them is interested in ACTIONMOVIES and one is interested in ROMANCEFILMS and one is interested in BASKETBALL, etc. My AI routine now needs to pick something for them to go do together and it should probably be something they're both interested in.

Set of things they're both interested in: SimA.interests & SimB.interests

MUCH more efficient than iterating through each flag and checking if it's in both of them. Orders of magnitude more efficient.

ShadauxCat avatar Feb 23 '15 13:02 ShadauxCat

Also, I tried var intersection = AB & BC; in interactive mode as well, and it worked fine. I'm using the latest code pulled from the git repository.

I've tried it with the latest revision from the fossil repo (it's the primary one). Doesn't work.

But you are right, this may come in handy sometimes. Apparently, the operation was meant to be supported anyway.

Night-walker avatar Feb 23 '15 14:02 Night-walker

That's strange. I just went to try it again and now it doesn't work. I wonder what I did to get my console into a state where it was working before.

ShadauxCat avatar Feb 23 '15 15:02 ShadauxCat

(dao) enum MyEnum { A; B; C; }
= none
(dao) var AB = MyEnum.A + MyEnum.B
= none
(dao) var BC = MyEnum.B + MyEnum.C
= none
(dao) AB & BC
[[Error::Value]] --- Invalid value:
invalid operands
In code snippet:
      1 :  GETVG       :     0 ,     3 ,     1 ;     1;   BC
>>    2 :  BITAND      :     0 ,     1 ,     2 ;     1;   AB & BC
      3 :  RETURN      :     2 ,     1 ,     0 ;     1;   AB & BC
Raised by:  __main__(), at instruction 2 in line 1 in file "interactive codes";
(dao) AB2 = MyEnum.A + MyEnum.B
= $A$B(3)
(dao) BC2 = MyEnum.B + MyEnum.C
= $B$C(6)
(dao) AB2 & BC2
= $B(2)

Apparently it works if you do not use the var keyword, but does not work if you do use the var keyword.

(dao) meta.typeOf(AB)
= type<MyEnum>_21_0x25bd7c0
(dao) meta.typeOf(AB2)
= type<MyEnum>_21_0x25bd7c0

No difference between them that I can see.

ShadauxCat avatar Feb 23 '15 15:02 ShadauxCat

I've found some information on what's going on with this one...

In DaoProcess_DoBitLogic() I'm seeing checks to ensure that the types are the same. What I see is two things:

  1. When doing this with enum literals, DaoProcess_GetEnum() is returning NULL. In this case self->activeTypes[0], which is what's being accessed to get this type, is NULL
  2. When doing it with enums declared with the var keyword, DaoProcess_GetEnum() is returning an enum with a different type than the two enums being operated on. Strangely in this case, self->activeTypes[0] is the same as self->activeTypes[1] is the same as self->activeTypes[2] is the same as the value returned, and self->activeValues[0]->xEnum.etype and self->activeValues[1]->xEnum.etype are also the same value as each other... but they're not the same value as the values in self->activeTypes

This is the line that throws the error in both cases:

        if( en == NULL || en->etype != A->xEnum.etype )
            goto InvalidOperation;

ShadauxCat avatar Apr 04 '15 17:04 ShadauxCat