Skip to content

Implementations allowing Ext(c)?.foo(). #677

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

Closed
lrhn opened this issue Nov 11, 2019 · 5 comments
Closed

Implementations allowing Ext(c)?.foo(). #677

lrhn opened this issue Nov 11, 2019 · 5 comments
Labels
bug There is a mistake in the language specification or in an active document extension-methods

Comments

@lrhn
Copy link
Member

lrhn commented Nov 11, 2019

See dart-lang/sdk#39325 for context.

The specification of extension methods states that Ext(c)?.foo is disallowed because it treats Ext(c) as a value in order to check whether it's null.

The language implementations (front-end and analyzer) accepts it as implicitly meaning (tmp = c) == null ? null : Ext(tmp).foo(). That is, they treat Ext(c) as having the same value as c, just with different "members", and applies the ?. to that.

That behavior is not unreasonable. It allows users to avoid applying extensions to null inline (without a separate x != null ? ...), even though all extensions currently accept null.
If we ever move to "extension types" where you can write var tmp = Ext(c); tmp.foo();, it would still make sense to allow tmp?.foo() because at that point Ext(c) will have a value: the same value as c but with a different static only type. Checking the actual value for being null makes sense then, and allowing Ext(c)?.foo() is consistent with that view (evaluate c to a value, statically cast it to the extension type, then do ?.foo() on that value which might be null).

So, should we keep the currently implemented behavior and amend the specification?

@lrhn lrhn added bug There is a mistake in the language specification or in an active document extension-methods labels Nov 11, 2019
@eernstg
Copy link
Member

eernstg commented Nov 11, 2019

We decided that Object methods should be callable on receivers whose type is nullable (that's Object? methods, of course, which is the reason why this is sound). So x.toString() is OK for a nullable x.

The case for extension methods is even stronger: It has been one of our standard examples for extension methods that it can be on a nullable type, and this can be used to give special treatment to the case where the receiver is null.

So x.foo() should surely be allowed in the case where x has a nullable (or potentially nullable) type T which does match the on clause of the extension that declares foo.

In general, the nnbd feature spec says that 'it is a warning to use a null aware operator (?., ?.., ??, ??=, or ...?) on a non-nullable receiver'.

We could extend this to say that it is then also a warning (or similar) to use x?.m when m is a member of Object?, because x.m would be OK, and similarly for extensions. But we don't do that currently.

This means that we currently support avoiding to execute an Object? member using ?., e.g., x?.toString() where the type of x is potentially nullable.

For consistency, we should then also allow x?.foo() to cancel the invocation of an extension method when x is null, even in the case where the on type is nullable.

This is consistent with the behavior of the front end as well as the analyzer today:

extension E on A? {
  foo() { print("foo!"); }
}

class A {}

main() {
  A? a = null;
  a?.foo(); // OK.
}

With that in mind, I think the most important goal here should be to make sure that an explicit extension method invocation like Ext(c)?.foo() is consistent with the implicit form c?.foo().

This means that the spec should either continue to make Ext(c)?.foo() an error, or it should specify explicitly that this form (and similar null-aware forms) are allowed and will skip the execution of the member access and evaluate to null, when the receiver is null.

@leafpetersen
Copy link
Member

leafpetersen commented Nov 12, 2019

I don't see any real issue with allowing it. It doesn't really particularly commit us to Ext(c) having any particular value: one can view Ext(c)?.<selectors> as just syntax for "check that c is not null, and so long as it is not, execute Ext(c).<selectors> and otherwise return null.

@lrhn
Copy link
Member Author

lrhn commented Nov 13, 2019

That would be the meaning, yes. So, seems like we are leaning towards accepting it. I'll update the design document to match.

@leafpetersen
Copy link
Member

It's decided then, we will allow this.

eernstg added a commit that referenced this issue May 6, 2020
This PR introduces text corresponding to the decision in language issue #677: It is allowed to use expressions of the form `E(o)?.id` (and similarly for all other kinds of null-aware member access) to access an extension member from the extension `E` for the receiver `o`.
@eernstg
Copy link
Member

eernstg commented May 6, 2020

Specification adjusted in #953. It is already supported by implementations, so I'll close the issue.

@eernstg eernstg closed this as completed May 6, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug There is a mistake in the language specification or in an active document extension-methods
Projects
None yet
Development

No branches or pull requests

3 participants