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

Run toString(null) for explicit nullable abstract #12019

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

Conversation

tobil4sk
Copy link
Member

If the abstract is declared over Null<T>, then null is part of the type so the user should be able to handle the null case within the toString method. It is also useful to be able to have custom empty values for an abstract that wraps a nullable type.

This is also in-line what null safety has to say, as it forces the user to add a null check to toString when the concrete type is Null<T>.

If the abstract is declared over Null<T>, then null is part of the type
so the user should be able to handle the null case within the toString
method. It is also useful to be able to have custom empty values for
an abstract that wraps a nullable type.

This is also in-line what null safety has to say, as it forces the user
to add a null check to toString when the concrete type is Null<T>.
@Simn
Copy link
Member

Simn commented Feb 19, 2025

There's also this case:

private abstract MyStringT<T>(T) from T {
	function toString() {
		if (this == null)
			return "EMPTY";
		return "FULL";
	}
}

function main() {
	var ms:MyStringT<Null<String>> = null;
	trace(ms);
}

Instead of using a_this directly we can use Abstract.get_underlying_type, which I think addresses this.

@Simn
Copy link
Member

Simn commented Feb 19, 2025

We discussed this and agreed that the function shouldn't be called in my example. The reasoning is that without the abstract itself being defined over Null<T>, it is unlikely that its functions were written with null-values in mind.

@tobil4sk
Copy link
Member Author

Using Abstract.get_underlying_type would also solve the following case:

private abstract MyString(Null<String>) from Null<String> {
    function toString() {
        if (this == null)
            return "EMPTY MyString";
        return this;
    }
}

private abstract MyStringWrapper(MyString) from MyString {
    function toString() {
        if (this == null)
            return "EMPTY MyStringWrapper";
        return Std.string(this);
    }
}

function main() {
    final a:MyString = null;
    trace(a);
    final b:MyStringWrapper = null;
    trace(b);
}

After further discussion, it seems that in the abstract A<T>(T) case, the unconstrained T is too abstract to allow doing anything (sensible) that could break when null is passed. However, it gets more complicated when constraints are involved, since Null<T> currently satisfies : T: #12020.

Perhaps we can find a solution for the constraint problem in #12020, in which case a non-nullable type constraint will guarantee that the external null check would be generated. Otherwise a more complicated workaround will be needed.

@skial skial mentioned this pull request Feb 20, 2025
1 task
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.

2 participants