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

Correct ConnectFlags classification (enum -> bitfield) #1002

Open
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

Bromeon
Copy link
Member

@Bromeon Bromeon commented Jan 6, 2025

Works around ConnectFlags being exported as enum instead of bitfield in Godot.

Also adds a general mechanism in special_cases.rs to override this property.

@Bromeon Bromeon added quality-of-life No new functionality, but improves ergonomics/internals c: engine Godot classes (nodes, resources, ...) labels Jan 6, 2025
@GodotRust
Copy link

API docs are being generated and will be shortly available at: https://godot-rust.github.io/docs/gdext/pr-1002

// Manual fills

// Note: this may cause import ambiguities for trait methods, however those are considered "minor changes" in Rust SemVer (which means
// patch bump for 0.x), in contrast to removing a trait impl: https://doc.rust-lang.org/cargo/reference/semver.html#item-new
Copy link
Member

@lilizoey lilizoey Jan 6, 2025

Choose a reason for hiding this comment

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

I dont think this is correct? This seems to mention both adding a new trait and an implementation for that trait. However this is implementing a trait for a type that previously did not have that trait implemented, but the trait already existed beforehand. The justification for why that change isn't considered breaking is because it requires a blob import to cause breakage, however you can cause breakage without a blob import when implementing a trait for a type that previously didn't have it. For instance this code would break with this change:

use godot::classes::object::ConnectFlags;
use godot::obj::EngineEnum;

pub trait MyTrait: Sized {
  fn ord(self) -> i32 {
    10
  }
}

impl MyTrait for ConnectFlags {}

fn foo() {
  _ = ConnectFlags::DEFERRED.ord();
}

as the call to ord is now ambiguous.

Copy link
Member

@lilizoey lilizoey Jan 6, 2025

Choose a reason for hiding this comment

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

ok here's a link where it is actually explicitly allowed: https://github.com/rust-lang/rfcs/blob/master/text/1105-api-evolution.md#minor-change-implementing-any-non-fundamental-trait
not sure why it's not on that semver documentation page

i guess that it being mentioned in that RFC as deferring to adding a default trait item means that this case actually falls under this point: https://doc.rust-lang.org/cargo/reference/semver.html#trait-new-default-item

which means we should discuss whether this is considered acceptable breakage? personally i'd say probably yeah, worst case breakage in this case would likely just mean someone made their own reimplementation of EngineEnum for ConnectFlags, in which case they can just remove that and their code continues working.

Copy link
Member Author

Choose a reason for hiding this comment

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

Thanks for double-checking this! I added your link as well.

I'd also say "minor" breakages should generally be considered acceptable. Otherwise we're walking a minefield and likely have to push the major (in our case minor) version for lots of situations where we just add things 🤔

Copy link
Member

@lilizoey lilizoey Jan 6, 2025

Choose a reason for hiding this comment

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

Well the trait-new-default-item category considers it "possibly breaking" and not "minor", so if it does fall in under that category then it's not automatically minor.

Copy link
Member Author

Choose a reason for hiding this comment

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

You're right, my mistake. Either way I think we should probably consider this case by case, there are some obscure APIs that have hardly any impact, and others which are used by almost every user.

I wrote a bit about this concrete case in this comment.

@Bromeon Bromeon force-pushed the qol/enum-bitfield-override branch from 29ed16b to 26af4c8 Compare January 6, 2025 20:35
@Bromeon
Copy link
Member Author

Bromeon commented Jan 6, 2025

The irony here is that removing EngineEnum would likely cause less breakage.

There are some fun parts leading to this:

  1. ConnectFlags is used exclusively around signal connecting.

  2. Object::connect_ex() requires u32.

  3. Signal::connect() requires i64.

    • This one is used more rarely, because with the intermediate object, it makes signal setup even more unwieldy than it already is...
  4. EngineEnum::ord() returns yet another type, i32 😀

  5. People who work with connect flags thus have to use as-casts in addition to ord(). It's not enough that we don't provide type-safe ConnectFlags, but even the underlying integer types mismatch 😁

    People who use it do so almost certainly in this way:

    ConnectFlags::DEFERRED.ord() as u32
    // or 
    (ConnectFlags::DEFERRED.ord() | ConnectFlags::PERSIST.ord()) as u32
  6. If we now add EngineBitfield but keep EngineEnum, we'll create an import ambiguity because both EngineEnum and EngineBitfield are in the prelude, which most people use.

TLDR: we're breaking more code by following SemVer guidelines than by violating them.

I'm not sure if we do users a favor if we include this in v0.2? Without type-safe connect functions, this is not that useful... it might be relevant for #1000 though 🤔

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
c: engine Godot classes (nodes, resources, ...) quality-of-life No new functionality, but improves ergonomics/internals
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants