Skip to content

Don't invent a default value when no well-defined value is availableΒ #3331

Open
@eernstg

Description

@eernstg

Cf. #2269 (comment).

Consider the following example (a variant of the example in the issue mentioned above):

class C {
  void foo([int x]);
  noSuchMethod(i) => print(i.positionalArguments[0]);
}

void main() => C().foo();

This program has no compile-time errors (that's fine, none were expected), but the run-time behavior depends on the configuration: dart runs the program and prints 'null', but DartPad and dart compile js generate code where the execution stops before the print expression because null is not a type correct actual argument when the parameter has type int.

First, please make a clear distinction: C.foo.x has no default value. If the declared type had been int? then it would have had the default value null. If it had been declared with an explicit default value d then it would have had the default value d. In other words, an implicit null is just as much a default value as an explicitly declared default value, but it is possible for an optional parameter (of an abstract declaration) to have no default value at all.

I'd recommend that we specify a dynamic error as follows:

Assume that a class C has an implicitly induced noSuchMethod forwarder D for a method named m (because it is concrete and has a non-trivial noSuchMethod implementation). Such noSuchMethod forwarders can be inherited, too, but we only need to specify the case where the forwarder is introduced. We could also have a noSuchMethod thrower, but they are not relevant for this issue.

Assume that D is invoked, and no actual argument is provided for a given optional formal parameter p (which could be positional or named). A dynamic error will then occur in the case where the default value of p is underspecified:

  • p has no default value because C contains an abstract declaration of m where no default value is specified for p, or m is in the interface of C because one or more direct superinterfaces have a member named m, and the most specific underlying declaration does not declare a default value for p for any of them.
  • p has an ambiguous default value because multiple direct superinterfaces of C have a member named m whose most specific underlying declaration(s) have multiple non-identical default values for p.

The default value can always be made available and unambiguous by putting an abstract declaration of m into C, specifying the desired default value. This implies, for example, that it is possible to resolve missing or ambiguous default values in superinterfaces without writing an actual method implementation. This could be useful in the declaration of a mock class.

This approach could be described as "fail late". We could also make it a compile-time error in every case where a noSuchmethod forwarder is being implicitly induced, and one or more optional parameters do not have a well-defined default value; that could be described as "fail early".

In both cases the change would be at least somewhat breaking: The current behavior in the VM allows noSuchMethod to be invoked with null as an actual argument, even in cases where that would be a soundness violation in an explicit declaration of the forwarded method, but with this change it would become a dynamic error, or even a compile-time error.

With an ambiguous value, the VM currently makes a choice (perhaps using the value from the textually first superinterface that specifies a default value), which may or may not be the "right" choice. It is again a breaking change to start raising a dynamic error in the ambiguous case.

@dart-lang/language-team, WDYT? Do we need to change the current behavior? Fail early or late?

Metadata

Metadata

Assignees

No one assigned

    Labels

    questionFurther information is requestedspecificationtechnical-debtDealing with a part of the language which needs clarification or adjustments

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions