We currently do not allow a void-typed expression to be used in a context where its value is used (only a context of type void or dynamic, and dynamic was only allowed to not have to rewrite legacy code).
We should consider whether void is allowed to flow into pattern matches.
We are already doing something on some platforms, but we should be consistent and deliberate about it.
I'd say "hard no", and disallow (aka. make compile-time error):
- Any
switch expression with static type void.
- Any
if-case matched value expression with static type void.
- Any pattern being type-checked with matched value type
void, unless the pattern is _ (or any other non-checking, non-binding pattern which also wouldn't need to look up the value of a list or map at all). That's needed for var (x, _, z) = o as (int, void, int);.
The first two are really just expressions-in-position-where-value-is-used, and could be covered by current behavior.
The last one is new, because there is no expression the original program which has static type void. It only exists in the desugared version. Still, the is a value with static type void which is pushed into a pattern match, and that pattern match actually looks at the value (it binds it to a new variable, one not typed as void).
We an allow a pattern to bind a void value to another variable of type void or dynamic, but you really shouldn't.
I'd prefer to make the void protection stronger here, since it's new syntax that doesn't have to accommodate existing code.
- Also disallow using
void as the type of a binding pattern, void v, to introduce a void-typed variable. Just use _, because you are not supposed to look at the value later anyway.
And
- Disallow
void as the static type of an object pattern. (Also if doing so through a type alias, but T(:var hashCode) is OK, even if T ends up bound to void at runtime. This is just static checking.)
Even though the object pattern void() is technically not looking at the object, it's also not useful, and you should just use _.
Doing void(:var runtimeType) is just plain wrong, you're invoking members on a void typed value. So
(Now that we have _, we no longer need to allow you to assign void values to anything. Or at least, we won't when we allow _ as a parameter name.)
Example code:
typedef OQ = Object?;
void main() {
void v;
switch (v) {
// Error: This expression has type 'void' and can't be used.
// switch (v) {
// ^
case OQ(:var hashCode): print(hashCode);
}
if (v case Object? o) print(o.hashCode);
// Error: This expression has type 'void' and can't be used.
// if (v case Object? o) print(o.hashCode);
// ^
if ((1, v) case (int _, Object? o)) { // No error
print(o.hashCode);
}
if (null case void v) { // Safe, but unnecessary
print(v as dynamic);
}
if (null case Void(:var hashCode)) { // Reads `.hashCode` of `void`
print(hashCode);
}
}
This was run in dartpad.dev on master branch.
Dart2js rejects two of the cases, but not the third.
The analyzer gives no warnings whatsoever. (Should probably be considered a bug.)
@dart-lang/language-team
We currently do not allow a
void-typed expression to be used in a context where its value is used (only a context of typevoidordynamic, anddynamicwas only allowed to not have to rewrite legacy code).We should consider whether
voidis allowed to flow into pattern matches.We are already doing something on some platforms, but we should be consistent and deliberate about it.
I'd say "hard no", and disallow (aka. make compile-time error):
switchexpression with static typevoid.if-casematched value expression with static typevoid.void, unless the pattern is_(or any other non-checking, non-binding pattern which also wouldn't need to look up the value of a list or map at all). That's needed forvar (x, _, z) = o as (int, void, int);.The first two are really just expressions-in-position-where-value-is-used, and could be covered by current behavior.
The last one is new, because there is no expression the original program which has static type
void. It only exists in the desugared version. Still, the is a value with static typevoidwhich is pushed into a pattern match, and that pattern match actually looks at the value (it binds it to a new variable, one not typed asvoid).We an allow a pattern to bind a
voidvalue to another variable of typevoidordynamic, but you really shouldn't.I'd prefer to make the
voidprotection stronger here, since it's new syntax that doesn't have to accommodate existing code.voidas the type of a binding pattern,void v, to introduce avoid-typed variable. Just use_, because you are not supposed to look at the value later anyway.And
voidas the static type of an object pattern. (Also if doing so through a type alias, butT(:var hashCode)is OK, even ifTends up bound tovoidat runtime. This is just static checking.)Even though the object pattern
void()is technically not looking at the object, it's also not useful, and you should just use_.Doing
void(:var runtimeType)is just plain wrong, you're invoking members on avoidtyped value. So(Now that we have
_, we no longer need to allow you to assignvoidvalues to anything. Or at least, we won't when we allow_as a parameter name.)Example code:
This was run in dartpad.dev on master branch.
Dart2js rejects two of the cases, but not the third.
The analyzer gives no warnings whatsoever. (Should probably be considered a bug.)
@dart-lang/language-team