sdk
sdk copied to clipboard
Super void parameter throws This expression has type 'void' and can't be used
Repro:
class A {
A(this.value);
A.named({this.value});
final int? value;
}
// Reason for the constructors to all have the same parameters would be consistency and tear-off possibility
class B extends A {
B(super.value);
B.named({super.value}) : super.named();
}
// Here everything is fine
class C extends A {
C(void value) : super(0);
C.named({void value}) : super.named();
}
class D extends C {
D(super.value);
D.named({super.value}) : super.named();
}
void main() {
D(0);
D.named(value: 0);
}
Error:
lib/a.dart:19:18: Error: This expression has type 'void' and can't be used.
a.dart:19
D.named({super.value}) : super.named();
^
Old
The problem here is that there is no analyzer warning at D.named({super.value}), and the error happens when running. This can be fixed if we simply replace super.value with void value, but that is harder IMO.
The problem here (considering lrhn's first comment on this issue) is that the CFE is wrong. We should be able to compile the code. The analyzer shows no diagnostics and this is WAI.
@lrhn or @eernstg:
This can be fixed if we simply replace
super.valuewithvoid value, but that is harder IMO.
Why using super is not allowed for this case?
It should be allowed.
The void rule says that an expression with static type void is an error unless the expression is in one of a number of allowed positions.
The super.value is a parameter, not an expression.
If the super parameter is desugared to something like ({void value}) : super(value: value), then that's an implementation detail, and not something the user wrote. The user code had no error.
We should never report errors in code that we introduced to begin with.
I've noticed that:
class E extends C {
E(void value) : super(value); // use_super_parameters
E.named({void value}) : super.named(value: value); // use_super_parameters
factory E.other(int value) {
if (1 == 1) {
return E(value); // void_checks
}
return E.named(value: value); // void_checks
}
factory E.last(void value) {
if (1 == 1) {
return E(value); // nothing
}
return E.named(value: value); // nothing
}
}
void main() {
E(null);
E.named(value: null);
}
But we still get this inside E.last (more below at *):
Error: This expression has type 'void' and can't be used.
a.dart:39
return E.named(value: value); // nothing
^
Weirdly, nothing about the above positional case. Also, should we have warnings in the analyzer for the cases inside E.last?
*This is weird, I removed the named parameter cases and the code compiled normally. So it seems that even if the value void is used on positional cases is fine, just not in named cases?
I believe the places where a void expression is allowed includes (regrettably and for historical reasons) return expressions of void functions and argument expressions for void parameters.
(You should really just use null in both cases.)
The void_checks lint is separate, it warns about passing a non-void, non-null value to a void parameter, or return from a void function. The language allows anything to be assigned to void.
So it's extra weird and wrong that the original example complained about the D.named constructor, because even its desugared version only uses the variable as argument to a void parameter.