-
Notifications
You must be signed in to change notification settings - Fork 226
Description
[Edit: The language team decided on Jan 3 2024 that the answer is "No" (see this comment).]
See dart-lang/sdk#51517 for some background information.
The language specification has specified for at least 6 years that an invocation of the form e<typeArgs>(args)
where e
has static type dynamic
must handle a callee o
(the value of e
) which is not a function object in the following way:
- Check that
o
is an instance of an interface type that has a method namedcall
. - Execute
o.call<typeArgs>(args)
.
(and this includes a similar rule where there are no actual type arguments and/or no actual value arguments because "typeArgs
and args
can be empty".)
However, the implementations (at least the VM, the JavaScript output from dart compile js
, and the executable provided by dart compile exe
, on x64) supports the following scenario as well:
- Check that
o
is an instance of an interface type that has a getter namedcall
. - Invoke that getter,
o.call
, to obtain an objectfo
. - Execute
fo<typeArgs>(args)
(which may run the same algorithm recursively).
The language team has had discussions about this behavior previously (a long time ago), and did not support the generalization. That is, the language team does not want the second scenario to be supported.
However, many backends (perhaps even all of them?) support the second scenario, and @mraleph suggested in dart-lang/sdk#51517 that we should change the specification such that the second scenario is allowed: (1) This is the most convenient decision because this behavior is implemented today, and (2) it would be a breaking change to stop supporting scenario 2.
I believe we had some arguments about the potential performance implications of supporting scenario 2, but at this time I can't see any reason why this would be important. In particular, I can't see why supporting scenario 2 would cause function invocation to be slower in all cases, or function objects would occupy more space in all cases; it seems more likely that it just makes dynamic function invocations more expensive, and they are already allowed to be expensive because we assume that they are rare (and should be even rarer ;-).
@dart-lang/language-team, WDYT? Should we change the specification such that scenario 2 is supported? Alternatively, should we initiate a breaking change process about scenario 2 being unsupported in the future?
@mkustermann, @mraleph, @sigmundch, @nshahan, @askeksa, @osa1, WDYT? Are you aware of any optimization opportunities that are made impossible in the case where the language supports scenario 2? And assuming that it is correct that we do this already, it's more like: Are you aware of any optimization opportunities that we could benefit from in the future if we stop supporting scenario 2?
@johnniwinther, WDYT? As far as I understand @osa1's comment, the CFE generates code where it is impossible to make the distinction between the invocation of a method and the invocation of a getter that yields a function object which is subsequently invoked. I thought that this had now been separated out into two distinct kinds of Kernel code, and also that it had been beneficial for function invocation performance in general to make this distinction explicit in Kernel code. Is this situation special because it is an invocation of an object of type dynamic
?