Skip to content

GDScript: Replace abstract keyword with @abstract annotation #107717

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

Merged
merged 1 commit into from
Jun 27, 2025

Conversation

aaronfranke
Copy link
Member

@aaronfranke aaronfranke commented Jun 19, 2025

This was requested by @vnen and discussed in the GDScript meeting on 2025-06-17. Meeting notes:

Screenshot 2025-06-19 at 6 37 20 AM

This effectively restores the code to an earlier version of PR #67777, except that it includes abstract functions as added by PR #106409.

# Before:
abstract class_name MyBaseClass

# After:
@abstract class_name MyBaseClass

Why?

Disclaimer: I am in favor of this PR, so this list is probably biased.

Reasons For

  • Makes more sense from a language design perspective: @abstract is a modifier that modifies a class, class_name, or func. It can't be used by itself.
    • See @dalexeev's comment here for a more detailed explanation of this.
  • Arguably makes more sense from the perspective of a user reading GDScript code, since it clearly separates the keywords that declare a thing compared to the keywords that modify declared things.
    • This especially improves readability when considering syntax highlighting.
  • Consistency with possible future annotations like @override and @virtual which go well with @abstract.
  • Simpler to implement, requires a lot less special case code in the GDScript codebase.
  • Similar to some programming languages, like Python.

Reasons Against

  • Different from many languages, such as C#, Java, Kotlin, etc.
    • This may counteract the "Arguably makes more sense ..." part above, because if users are used to abstract from other languages, then abstract would be less friction. (possibly? only slightly?)
  • Some users and engine maintainers have expressed that this "feels" like it should be a keyword.
    • I forget @vnen's exact words but he said something along the lines of that this wasn't good justification and nobody could come with a clear precise reason for why this should be a keyword, while the fact of it being a modifier is an objective easy-to-measure thing.
  • Short-term: Inconsistency with static.
    • static is planned to be replaced with @static. Since removing static would break compatibility, we might add @static before removing static so both work for awhile.
  • Short-term: Affects projects running the engine's master branch and already using abstract.
    • We don't support compatibility between different revisions of master, so this is a hard breakage.
  • Short-term: Breaks the already-updated GitHub syntax highlighting for GDScript.
    • I would expect this to be resolved in a short timespan.

Neutral

  • For functions, this requires allowing the parser stage to parse bodyless functions.
    • This changes the abstract errors to analyzer errors instead of parser errors.
    • This means that a void function with no body is effectively identical to : pass except that abstract functions explicitly cannot have a body and so : pass is forbidden there and non-abstract functions must have a body.

Godot isn't a democracy, but you can react with 👍 and 👎 to show your support for or against this change.

@aaronfranke aaronfranke added this to the 4.5 milestone Jun 19, 2025
@aaronfranke aaronfranke requested review from a team as code owners June 19, 2025 14:08
@aaronfranke aaronfranke requested a review from a team as a code owner June 19, 2025 14:08
@aaronfranke aaronfranke force-pushed the abstract-annotation branch 2 times, most recently from be946d4 to 7767d3f Compare June 19, 2025 14:35
@dalexeev
Copy link
Member

dalexeev commented Jun 19, 2025

and it also un-bumps the bytecode version number that was bumped by PR #106552

There is #82808, so there is no need to un-bump.

@romgerman
Copy link

Hey! This PR feels more complete than #107713 because it has an explanation. I understand that it's "too late" to be trying to stop the moving train but still I would like to express my thoughts.

I'm all for UX, consistency, minimalism, readability and legibility but I must say these reasons don't really justify the change IMO.

I agree with the fact that abstract keyword is a modifier, an access modifier, that cannot be used my itself. But I'm not sure if I can agree with using this fact as a reason for the change. There are other keywords in GDScript that cannot be used by themselves like extends and static.

From readability standpoint (keyword vs annotation) I think it really depends on the user, on how they perceive code and how they write it.

What I really want to say is this. This is my opinion and a view of how I perceive annotations in GDScript:
Annotations do not change how user interacts with code (coding experience? I'm not really sure how to call it). There is currently no annotations which add conditions (constraints) in between the user and their code that dictate them how to write it. All of current annotations change external behaviors like how exported variables look inside the editor, how node script appears in the scene tree, does a property gets stored in a file or not, and so on. The only exception that comes close is perhaps @ready, though that's debatable.
But then when you use @abstract annotation on:

  • a class, then you cannot instantiate that class;
  • a class method, then you must override that method in the child class;
  • a class method in a class that has no @abstract, then it will produce an error unless you annotate that class too.
    And then other annotations like @virtual and @override are going to change this further.

That was what I wanted to say. If annotations are moving towards becoming a more generic system (allowing users to create their own like decorators in JS, attributes in C#, annotations in Java, etc.) then I'm all for it.

@produno
Copy link

produno commented Jun 19, 2025

I have always assumed annotations are used for anything that affect the editor in some way and keywords were for things that affect the code. So this change seems confusing to me.

I shall give some quick (vague) examples:

@export - Shows in the inspector panel. (Interacts with the editor)
@icon - Gives the class an icon. (Interacts with the editor)
@tool - Allows the class to run in the editor. (Interacts with the editor)
@warning_ignore - Suppresses warnings in the editor. (Interacts with the editor)
@onready - Retrieves the node path from within the scene tree upon the node being ready. (interacts with the editor)

class_name - Declares a global class name for the script. (Interacts with the code)
static - Defines a class level var or func. (Interacts with the code)
extends - Declares inheritance. (Interacts with the code)
etc.

I think having abstract as an annotation deviates from this and is a little confusing.

@jordedwards
Copy link

jordedwards commented Jun 19, 2025

I somewhat disagree with the above comments, I don't think it's fair to say that annotations are exclusive to the editor.

@export and @ready can both fundamentally change the behaviour of a variable/property as they affect its lifecycle, just because this part is "hidden" and due to the VM interpretation (or in the case of C#, injection of values into the class instance) they are still "core" to the language behaviour.

My only gripe with the reasoning outlined here is that, following the "Reasons For", doesn't this also apply to the static keyword? I'm struggling to see exactly why "abstract" would fall into the annotation bucket while "static" wouldn't fit the same criteria here.

I do think this reason

Simpler to implement, requires a lot less special case code in the GDScript codebase.

is quite valuable, simplicity of the underlying code is immensely valuable, especially in a project where open contributions are encouraged and things like this can improve coprehensibility.

@geekley
Copy link

geekley commented Jun 19, 2025

Honestly, to me I already accepted that the difference between keyword and annotations in GDScript currently is too arbitrary anyway, so I'm like... whatever. I'd normally prefer keyword, but if virtual and override will be annotations, I prefer consistency more.


However, if there's ever a GDScript 3.0 (5.0?) on Godot 5, please make the annotation system more meaningful, with annotations only for things that need it, have a more clearly defined reason for annotations to exist.

I think everything in the language where its presence/absence check is mandatory (i.e. that breaks forward-compatibility) should be a keyword, even if it has functionlike(...) syntax.

Then make annotations some extensible system allowing custom classes and some hook system to modify the compilation generically like a preprocessor (but syntax-based, not text-based like C).
E.g. @MyCustomAnnotationClass(...) or @InBuiltOptionalAnnotation(...) that could generically use reflection, modify code about to be emitted, raise custom warnings or errors, all via GDScript code. By just adding the class/addon you'd get support for that behavior.
You could make it so e.g. UpperCase first letter means it's optional (forward-compatible, so unrecognized ones are silently ignored) and lowercase is mandatory check, e.g. @custom_annotation(...) would raise error if unrecognized (so not forward-compatible).

This would allow some GDScript features be introduced by addons, reducing some implementation weight on core devs. Core features could be introduced as inbuilt annotations when they can be forward-compatible, and as keyword otherwise. It would also facilitate testing these features by many users without recompiling the engine.

@produno
Copy link

produno commented Jun 19, 2025

virtual and override have not yet been introduced, and according to the link you sent me in a previous discussion here, they will be decided upon on a case by case basis.

It seems that contributors could not decide upon a consensus, so we all have to suffer the consequences of a mish mash design. Although it already seems like there's a pattern for what an annotation is or is not, so why not continue with that.

I guess that's unfortunately one of the issues with open source development.

@aaronfranke aaronfranke force-pushed the abstract-annotation branch from 7767d3f to bdeb83d Compare June 19, 2025 23:07
@aaronfranke
Copy link
Member Author

@dalexeev Dang, we worked on the same thing. I said in the GDScript meeting that I would work on this. Un-un-bumped, and I updated my PR to match the more detailed documentation in your PR. You and I took different approaches, though. I initially dismissed the approach in your PR because it's pretty much only a syntax change, and still keeps the "special case" code in the language parser. The approach in this PR is to make it behave like other annotations, which for functions, does involve moving the body-or-not check to the analyzer stage.

@romgerman extends can be used by itself, because class names are optional in GDScript. Also, @abstract is effectively editor tooling, since we don't need to check for instantiating abstract classes at runtime.

@romgerman @jordedwards The plan is also to replace static with @static. See the in-progress discussion in the GDScript channel on Godot's RocketChat for more information.

@aaronfranke aaronfranke changed the title GDScript: Replace abstract keyword with abstract annotation GDScript: Replace abstract keyword with @abstract annotation Jun 19, 2025
@aaronfranke aaronfranke requested review from vnen and dalexeev June 19, 2025 23:10
@jordedwards
Copy link

@aaronfranke gotcha, sorry for not clarifying before assuming static was treated differently

@JoNax97
Copy link
Contributor

JoNax97 commented Jun 20, 2025

I think static is the best argument against @abstract being an annotation.

Both are modifiers, both apply to both classes and methods, and both are language-level features, that don't interact with other parts of the engine (like most annotations do, as @produno said).

I understand the team's reluctance to add more keywords to the language, 3.X went too far into that direction and the ones that were turned into annotations in 4.0 are a good change. But I think this is going too far into the other direction.

@OhiraKyou
Copy link
Contributor

Here's how I've always interpreted the responsibilities of keywords and annotations:

  • Keywords are for language features.
  • Annotations are for adding custom features based on where the language is being used (e.g., a game engine or specific game project). Godot simply provides some out-of-the-box annotations to interface with the engine and editor.

As such, I fundamentally disagree with abstract, static, virtual, or override being annotations. I know that GDScript is developed specifically for Godot. But, I believe that the past language and implementation division of keywords and annotations was wise, even if it was unintentional.

@Lazy-Rabbit-2001
Copy link
Contributor

Lazy-Rabbit-2001 commented Jun 20, 2025

Based on vnen and my thought, the most reason why we treat some modifiers as annotations is because of the frustrating system of adding new keywords. Every time you add a keyword, you have to change a lot of files, even the core files of gdscript module. Don't believe it? Check the source file and see how these keywords work.

Meanwhile, the handle of keywords is much more difficult than annotations, which just require registration and implementation method during their definitions.

Adding more keywords means higher unreadability of the source code, increasing the difficulty of maintaining the engine module. Maybe we can dig deeper on this issue in GDScript 3.0 in the future and overhaul the whole keyword system to make it much more simpler to handle.

As for static being @static, we've discussed and agreed to keep both in the engine, while the keyword static will be deprecated with a warning. Dislike warnings? We are considering the update tool to help with converting all static keywords to annotations.

I admit that it is still ambiguous for most of us when a syntax element is a keyword and when it is presented as an annotation. But for modifiers in the middle of the both, we have to discuss more and reach a clear and determined consensus on this question.

@OhiraKyou
Copy link
Contributor

OhiraKyou commented Jun 20, 2025

All keywords modify some behavior in some way. Otherwise, they would just be white space. So, the very idea of modifiers as a distinct category is vague.

I much prefer the distinction between language features (provided by keywords) and engine features (exposed to the language through annotations). Annotations act as an extension system for the language, enabling context-specific features (i.e., those from an engine or game) to be injected into the language. Naturally, adding an extension is easier than modifying core files. If keyword implementation could be streamlined, that's a separate matter.

And, indeed, if keyword implementation is difficult, and a conversion is to be made, it makes far more sense to convert from annotation to keyword rather than from keyword to annotation. One could implement a language feature as a prototype annotation and then replace it with a keyword when it's ready.

@produno
Copy link

produno commented Jun 20, 2025

In fear of flogging a dead horse, I shall make one last comment.

As an end user, and selfishly so, I do not care how complex something is to implement and maintain. All I care about is the end user experience being as good as it possibly can be. I don't think usability should be sacrificed for less complexity, or at least, if such an integral part of the experience is difficult to maintain, then I guess that's an issue within itself. (Of course I understand this is not the case for everything.)

That said, I will respect the decision of the maintainers. I will just have nightmares of annotations littering my codebase.

@Ivorforce
Copy link
Member

Ivorforce commented Jun 20, 2025

I'll note something that I think has not been said yet. To me, the main motivation to make abstract a modifier rather than a keyword is this:

abstract does not affect the language grammar.

Making it a keyword forces parsers to resolve it immediately when building the syntax tree. This can bring problems, such as missing syntax highlighting, broken autocomplete, or unhelpful error messages while editing the code.

To provide the best user experience, the language grammar needs to be agnostic to contextual details, such as whether a function needs a body. That type of question is better answered during type and variable resolution.

I think some people feel strongly about abstract being a keyword because it is a keyword in many other languages. But when taking a step back, the best choice is obvious. The effect of making language parsers easier to implement, and more forwards compatible, is secondary (though welcome).

@AThousandShips
Copy link
Member

AThousandShips commented Jun 20, 2025

Beyond a few early adopters using 4.5.dev5 I don't see any usability issues with changing this from a keyword to an annotation, so there isn't really a trade off between code complexity etc. and user experience I can see (and while we should reduce friction as much as possible the development versions are unstable and that should be well known, so some friction is to be expected)

@OhiraKyou
Copy link
Contributor

OhiraKyou commented Jun 20, 2025

If the parser and variable resolution can't elegantly handle an abstract keyword, that sounds like a problem with the parser and variable resolution.

The usability issues, or friction, of an @abstract annotation comes from using a core language feature as if it were a context-specific (i.e., engine) feature injected into the language.

@Ivorforce
Copy link
Member

Ivorforce commented Jun 21, 2025

we'd just be arguing over whether we want the leading @ or not. Is it important to omit it if it works the same way? I personally think it doesn't matter, but having the @ is more truthful to how it actually works grammar-wise.

That the point. Language grammar is what is important for the user, not how it represented internally. Even if it works the same way, its representation is what the user will accustom themselves with.

Yes, that's the point I was making :) Language grammar is the important bit, not the internal implementation.
For good user experience, abstract must be an annotation in the language spec. That much is clear. A user wouldn't notice implementation differences (like you said), other than better error messages while editing.

Whether or not it has a leading @ is a different question, the reasoning of which I feel @dalexeev has summarized well.

Copy link
Member

@Mickeon Mickeon left a comment

Choose a reason for hiding this comment

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

This took me quite a moment

Comment on lines 302 to 304
An abstract class is a class that cannot be instantiated directly. Instead, it is meant to be subclassed by other classes. Attempting to instantiate an abstract class will result in an error.
An abstract method is a non-static member function that has no implementation. An abstract function has no body; instead, a newline or semicolon is expected after the function header. An abstract method defines an interface that subclasses must conform to, because the method signature must be compatible when overriding a base class method.
If a class has at least one unimplemented abstract method (either its own or inherited), then it must also be declared abstract. However, the reverse is not true; an abstract class may have no abstract methods.
Copy link
Member

Choose a reason for hiding this comment

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

Attempting some consistent language. The entire class reference is guilty of this, and I could explain myself further, but I don't know if I should in one relatively random reply.

Suggested change
An abstract class is a class that cannot be instantiated directly. Instead, it is meant to be subclassed by other classes. Attempting to instantiate an abstract class will result in an error.
An abstract method is a non-static member function that has no implementation. An abstract function has no body; instead, a newline or semicolon is expected after the function header. An abstract method defines an interface that subclasses must conform to, because the method signature must be compatible when overriding a base class method.
If a class has at least one unimplemented abstract method (either its own or inherited), then it must also be declared abstract. However, the reverse is not true; an abstract class may have no abstract methods.
An abstract class is a class that cannot be instantiated directly. Instead, it is meant to be inherited by other classes. Attempting to instantiate an abstract class will result in an error.
An abstract method has no implementation. Therefore, a newline or semicolon is expected after the function header. This defines an interface that inheriting classes must conform to, because the method signature must be compatible when overriding a method from the inherited class.
If a class has at least one unimplemented abstract method (either its own or inherited), then it must also be marked as abstract. However, the reverse is not true; an abstract class may have no abstract methods.

Or... I am struggling to keep the same information, but at the same time it's not the class reference's business to explain why abstract classes are so useful. I think I would prefer:

Suggested change
An abstract class is a class that cannot be instantiated directly. Instead, it is meant to be subclassed by other classes. Attempting to instantiate an abstract class will result in an error.
An abstract method is a non-static member function that has no implementation. An abstract function has no body; instead, a newline or semicolon is expected after the function header. An abstract method defines an interface that subclasses must conform to, because the method signature must be compatible when overriding a base class method.
If a class has at least one unimplemented abstract method (either its own or inherited), then it must also be declared abstract. However, the reverse is not true; an abstract class may have no abstract methods.
An abstract class is a class that cannot be instantiated directly. Instead, it is meant to be inherited by other scripts. Attempting to instantiate an abstract class will result in an error.
An abstract method must have no implementation, and a newline or semicolon after the method's declaration. When defining or inheriting a class, its abstract methods must be overridden and compatibile. If this isn't done, the class must be marked as abstract.
[b]Note:[/b] An abstract class may have no abstract methods.

Or something similar. You know this better.
What became a note in the above suggestion should ideally be placed below the [codeblock] example.

Alternatively, it's worth considering that oftentimes there's a distinction between "classes" and "scripts". Even if essentially script inheritance works just the same way as classes do, descriptions like these further muddy the water...

Copy link
Member Author

Choose a reason for hiding this comment

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

@dalexeev Thoughts? I just copied this from your PR's docs.

Copy link
Member

Choose a reason for hiding this comment

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

I guess inherited instead of subclassed sounds better. But I would like to use the term subclass more often (inherited class is fine but more verbose and child class should never happen).

Now, I would prefer class here instead of script. While every class in GDScript is also a script, users may not realize it also applies to nested classes. Since this is GDScript docs, it's obviously talking about GDScript classes.

Copy link
Member

Choose a reason for hiding this comment

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

Thoughts? I just copied this from your PR's docs.

I just copied this from another PR. :)

I don't have a strong opinion about documentation. Personally, I would like to point out that static methods cannot be abstract (because GDScript does not have late static binding, unlike PHP). I agree about script vs class. Also, maybe the example in codeblock should be improved.

Copy link
Member

@Mickeon Mickeon Jun 22, 2025

Choose a reason for hiding this comment

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

I guess inherited instead of subclassed sounds better. But I would like to use the term subclass more often

I must insist against "subclass" right now, if only to keep the terminology consistent. The term is uncommon compared to referring to the overall concept of inheritance.

child class should never happen

You'd be surprised. Unfortunately "child/parent class" is roughly as common as "subclass"

Copy link
Member

Choose a reason for hiding this comment

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

Not quite everything from #107717 (comment) has been addressed.

Inheriting classes must either provide implementations for all abstract methods, or the inheriting class must be marked as abstract. If a class has at least one abstract method (either its own or an unimplemented inherited one), then it must also be marked as abstract. However, the reverse is not true: an abstract class is allowed to have no abstract methods.

To make it brief, this final line is unusually verbose. keeping this in mind for later.

@Lazy-Rabbit-2001
Copy link
Contributor

Lazy-Rabbit-2001 commented Jun 22, 2025

For those who still have confusion on whether abstract acting as an annotation is less proper,

Annotations
Annotations are special tokens in GDScript that act as modifiers to a script or its code and may affect how the script is treated by the Godot engine or editor.

The documentation has clearly stated the usage of annotation, so in this case I don't think there is critical issue on converting abstract into annotation. As you've discussed, it's somewhat a problem of programming/language habbit/preference.

@OhiraKyou
Copy link
Contributor

OhiraKyou commented Jun 22, 2025

That description could also apply to keywords.

Modified:

[Keywords] are special tokens in GDScript that act as modifiers to a script or its code and may affect how the script is treated by the Godot engine or editor.

So, it still leaves the issue of distinction.

@aaronfranke aaronfranke force-pushed the abstract-annotation branch from 00d4748 to de77ce2 Compare June 23, 2025 16:38
Copy link
Member

@dalexeev dalexeev left a comment

Choose a reason for hiding this comment

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

Looks good to me.

Copy link
Member

@vnen vnen left a comment

Choose a reason for hiding this comment

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

Looks good to me. Thanks for working on this!

@akien-mga akien-mga merged commit ebc36a7 into godotengine:master Jun 27, 2025
20 checks passed
@akien-mga
Copy link
Member

Thanks!

@aaronfranke aaronfranke deleted the abstract-annotation branch June 27, 2025 19:56
@scgm0
Copy link
Contributor

scgm0 commented Jun 28, 2025

This PR has sparked so much controversy: 29 👍, 15 👎, yet why was it merged so quickly? Do 👍 and 👎 actually have any influence? Is the standard for merging a PR simply that the maintainer feels it's ready to merge?

Another PR I'm following, #76462, has 101 👍 and zero 👎. What's holding it back from being merged? Some mentioned it was due to the lack of a physics maintainer, but since then, several other physics-related PRs have been merged 😕.

All of this just feels odd to me.

@dalexeev
Copy link
Member

Do 👍 and 👎 actually have any influence?

@scgm0 Reactions are taken into account, but are not the only or most important criterion when making decisions. The main criterion is the consensus of the community and maintainers on whether we want to see a feature in the engine or not, as well as on how feasible, maintainable and consistent with the engine design and other features it is.

Sometimes, proposals that are highly supported by the community are difficult to implement or significantly conflict with the existing architecture and design of the engine. In the case of PRs, the reason may also be the lack of response from the maintainers, their temporary unavailability, etc. Something that prevents the production team from concluding that there is a consensus and the code is of good quality. Also, putting a thumb up or a thumb down is a matter of 5 seconds. We are all subject to emotional impulses, but not everyone spends at least 10 minutes to read the discussion and weigh the pros and cons.

In this case, there is a consensus among the maintainers that this change is good. Please note that this does not affect existing functionality, the abstract keyword does not exist in the stable version (4.4). The decision is only made regarding future modifiers for declarator keywords, the static keyword is not removed or deprecated (see also the related discussion). The arguments against this are taken into account, but are not considered convincing enough compared to the counter-arguments. In my opinion, the negative reactions in this case are more about subjective frustration of users that the language does not match their habits and the syntax of other languages, rather than about annotations as modifiers being objectively bad.

Finally, there is no solution that is without flaws and satisfies absolutely everyone. In my opinion, at least this criterion for dividing keywords vs annotations is the clearest and will allow us to avoid unproductive debates every time we want to add another modifier. See also my comment above.

Thank you for your question, but please note for future reference that PRs are not intended for meta-discussions about Godot merge policy and priority. It is fine to ask something about a specific PR, but nothing more. Closed PRs on the one hand attract less attention from contributors, but on the other hand can cause unnecessary notification noise for many participants. Alternatively, you can ask the question in the Contributors Chat in the general or specialized channel (in this case #gdscript).

@aaronfranke
Copy link
Member Author

@scgm0 Note that, in addition to what @dalexeev said, this is a new feature in Godot 4.5, and Godot 4.5 is releasing soon, already in beta. If we waited to do this, it would break compatibility to do it later. Therefore, this was an urgently high priority PR.

@scgm0
Copy link
Contributor

scgm0 commented Jun 28, 2025

From the response, the information I gathered is:

  1. Whether a PR gets merged is entirely up to the maintainers, and they apply different standards to different people. Generally, controversial PRs require sufficient justification to convince the maintainers, while for their own PRs, they only need to feel that the opposing arguments aren't reasonable enough.
  2. The swift merging of this PR was merely to make it a done deal before the official release of 4.5, so that even if the community voices strong opposition, the changes could be defended under the pretext of compatibility.

Godot is a community-driven project. Even if the above two points weren't the maintainers' original intent, these actions have left me feeling disheartened.

That's all. Regardless, I hope everyone has a wonderful day.

@akien-mga
Copy link
Member

akien-mga commented Jun 28, 2025

Yes, maintainers have the final say on what gets merged in the engine, if they have consensus among themselves. Input from the broader community is taken into account, and it was even discussed here, and all maintainers believe that the arguments made do not justify going against the direction chosen by the maintainers. We appreciate the feedback nonetheless.

You should see this as a minor adjustment of the PRs adding support for declaring abstract classes and methods in GDScript. If maintainers had agreed beforehand to use annotations for those, most likely the feedback you'd see on the PR would be a hundred something thumbsup like #67777 gathered, and maybe a handful of people arguing for it to be made a keyword. It actually even started with an annotation and there wasn't significant pushback for a whole 2 years. The "popularity contest" ratio would be quite different to what is now seen on a PR that's just tweaking the implementation details of an unreleased feature. In that hypothetical scenario, you might have then had someone propose a change from annotation to keyword after merge, and receive maybe 17 thumbs up and 29 thumbs down. What would we then do?

So we can't just operate by popularity contest, and popularity also doesn't translate to maintainability and people taking initiative to fix issues. So at the end of the day, the people who do take on responsibility to make the engine what it is over a long period of time (the maintainers) are the ones to make decisions on implementations.

@romgerman
Copy link

all maintainers believe that the arguments made do not justify going against the direction chosen by the maintainers

From the beginning (starting from #107713 which was made without stating any reasoning from the maintainers side except "we decided so") the reasoning from the maintainers has often felt inconsistent and underexplained. It felt like the maintainers weren't entirely sure why they decision was made themselves. And it comes up again and again throughout this discussion.

It actually even started with an annotation and there wasn't significant pushback for a whole 2 years.

But for some reason maintainers decided to make it a keyword and there were almost no pushback (see #67777 (comment)). There were still a discussion about whether it needs to be an annotation or a keyword and even that maybe it should be called virtual. But now out of blue maintainers decide to make it an annotation again based on their "objective" arguments (see #107717 (comment)). It does feel pretty hostile when the ultimate argument is "it's your SUBJECTIVE opinion and we are talking about it OBJECTIVELY" when maintainers are coming up with new excuses and say that their previous arguments are not what we should be discussing or even that their arguments are not based on any real problem (see #107717 (comment)).

What felt especially discouraging was the general tone toward users. At times it felt like we were going in circles, as if no matter what arguments were presented the outcome had already been decided. In the end it just seems pointless.

@akien-mga
Copy link
Member

akien-mga commented Jun 28, 2025

Well yes, as you point it out the maintainers had 3 years of discussion to come to a consensus. That consensus evolved over time and it finally reached a point which every maintainer is satisfied with.

Nobody is saying this is something objective. We discussed the subjective arguments in this very thread and GDScript maintainers presented their vision (which is consensual among other maintainers) for why this feature is better represented by an annotation than a keyword.

You can see it in @dalexeev's comment here: #107717 (comment)

What should be an annotation versus a keyword has been a source of internal discussion since annotations were introduced to Godot. There wasn't a clear design for it at the time, mainly the shared consensus that GDScript had too many keywords and that many of those would be better served by the concept of annotations. That change happened in Godot 4.0 (you can see that many of the annotations in Godot 4 were keywords in Godot 3).

After years of bikeshedding, the GDScript maintainers have come to a consensus for what annotations should be used for, and that's the one dalexeev described like this:

3. Keywords for declarators, annotations for their modifiers.

  • This rule is not followed for static and that many users are used to the fact that in other languages ​​these modifiers are keywords, not annotations/attributes/decorators (probably because the latter appeared in languages ​​later). However, at least this criterion is the clearest and would allow us to save ourselves from case-by-case debates about whether the next modifier should be a keyword or annotation.

That's it. It's a subjective consensus but we feel it's the best definition we have for what annotations should be, and to put an end to the endless bikeshedding about annotations versus keywords. We considered the feedback given here, discussed it, and that didn't change that resolution, so we proceed with the change.

Now we are going in circles with this discussion, I agree.

You need to understand that we're in the development phase for Godot 4.5, and anything that was merged so far for this milestone is still subject to change during the beta phase. It's totally normal for maintainers to change their mind about some implementation detail and correct it before a feature gets released in a stable release (after which it can't be changed anymore without breaking compatibility). We could also outright revert the support for abstract classes if its syntax is such an issue, but I doubt the 100+ upvotes and dozens of ecstatic commenters on social media would see it as a positive change either.

At the end of the day, Godot being community-driven doesn't mean that it's a tyranny of the majority (and here it's not even a majority pushing back on this change but a handful of users). The community provides feedback, bug reports, contributions, code reviews, etc. and helps define the direction of the project, e.g. by supporting the proposal to have abstract classes in GDScript. The actual decision on how do we implement features in a way that's maintainable for us in the long time belongs to maintainers. They're the one who take on the responsibility to make Godot what it is, and with great responsibility comes great power something something. So the decisions of maintainers are naturally weighed more heavily than that of commenters who have "less skin in the game", as maintainers are the ones who have to deal with the maintenance burden of anything that's added to the engine.

I think everything has been said here and we don't intend to continue painting that shed, so I'll lock the thread.

@godotengine godotengine locked and limited conversation to collaborators Jun 28, 2025
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

GDScript abstract class can be created in Node/Resource creation menu