adventuregamestudio / ags

AGS editor and engine source code
Other
696 stars 159 forks source link

New compiler: let omit type name when addressing static members from member methods #2067

Open ivan-mogilko opened 1 year ago

ivan-mogilko commented 1 year ago

New compiler already allows to omit the this keyword when addressing object's methods and fields from a object's method, but I found that when calling static methods from object's method you still have to type Typename.Function().

If possible, I propose to also let omit the typename in this scenario.

Unless this goes against the desired syntax design, in which case you may cancel this suggestion.

messengerbag commented 1 year ago

How does/would this resolve in the new compiler if the object member (instance or, in this proposal, static) matches a global function or variable? For example, given this struct:

managed struct Example {
  Character* player;
  static Hotspot* hotspot;  

  import void Wait(int time);
  import static Example* Random(int max);

  import void Test();
  import static void TestStatic();
};

… Would these references point to the internal members, or to the global functions and variables by the same name?

void Example::Test()
{
  Wait(10);  // AGS Wait() or this.Wait()?
  player.ChangeRoom(-1);  // AGS player or this.player?
}

void Example::TestStatic()
{
  hotspot = hotspot[Random(AGS_MAX_HOTSPOTS)]; // AGS Random() or Example.Random? AGS hotspot[] or Example.hotspot? (Fails to compile if AGS hotspot[]?)
}

Or will the compiler complain about clashing function/variable names?

And if it resolves to the local reference, is there any way to access the global one?

ivan-mogilko commented 1 year ago

I just tried it, and if struct's member matches the name of the global symbol, then compiler assumes the global symbol, so you must call it with this.

This is rather unexpected, and contradicts to how C/C++/C# deal with this. Looks like the order of resolving a name is reversed (starts with global symbols instead of local ones....)

ericoporto commented 1 year ago

While it's reversed, it makes sense as there's nothing you can put forward that points to the global scope but for the local scope you havethis keyword.

messengerbag commented 1 year ago

A consequence of this is that adding any new global functions or variables to the AGS API can unexpectedly change the behavior of existing code.

But perhaps global functions and variables are not supposed to be used (or, at least, introduced) in AGS 4?

ivan-mogilko commented 1 year ago

It's a good thing to put everything in a "namespace" (like a static class), but they will keep being there for a while, also noone can prevent people from writing modules with global functions.

I suppose that AGS script may benefit from global namespace accessor (like :: in C++) that would make scope distinction explicit where necessary.

fernewelten commented 11 months ago

To explain the current situation:

AGS doesn't currently have a good way of saying "take the global symbol, not the struct symbol", but it does have a good way of saying "take the struct symbol, not the global symbol" (namely, put this. in front of that symbol).

That's one of the reasons why when there is a global possibility as well as a struct possibility, the compiler prefers the global. If the programmer wanted the struct variable instead, they could use this. and say so.

The other reason is what the compiler actually does when it looks up symbols: It simply looks into its tables, and if the symbol is defined there, it considers the job done. But when it isn't defined in its tables, it doesn't give up just yet (as the old compiler did), but makes another try with this. in front of the symbol, and if that is defined, it takes that.

int X;

struct S {
    int X, Y;
    import function Foo();
}

function S::Foo()
{
    Display("%d", X);  // `X` is defined, it's that global up there in Line 1, so I'm going with it.
    Display("%d", this.X); // The X within the struct must be meant
    Display("%d", Y); // What? Don't know `Y`. Let's try `this.Y`. Ah that's defined?  Okay, use that.
}
fernewelten commented 11 months ago

So what do you want the compiler to do? Hide global X when this.X is defined? It can be done.

What about a local X? Would that hide the X in the struct?

int X;
struct S {
    int X;
    import function Foo();
}

function S::Foo()
{
   int X = 7;
   Display("%d", X); // Display the local `X`, the global `X` or `this.X`?
   // Currently, the compiler would find the local X first and consider the job done.
}
fernewelten commented 11 months ago

But perhaps global functions and variables are not supposed to be used (or, at least, introduced) in AGS 4?

As far as I know, global functions of the AGS interface are sort of deprecated: When new functions have been added to the interface, they were all under some umbrella type (if nothing fits, under Game). That must have already started in Chris Jones' time. What remains are some very old or very common functions such as Display().

ivan-mogilko commented 11 months ago

So what do you want the compiler to do? Hide global X when this.X is defined? It can be done. What about a local X? Would that hide the X in the struct?

Well, i believe that there has to be a universal unambiguous rule for which scope takes the precedence. And in my opinion the precedence should be given to a local scope, because that's a standard, and without it writing generic code will be a complicated task.