-
Notifications
You must be signed in to change notification settings - Fork 226
Description
Consider the following program consisting of two libraries:
// --- Library 'n045lib.dart' (version 3).
abstract class A {
int get _private;
}
void foo(A a) => print(a._private);
// --- Library 'n045.dart'
import 'n045lib.dart';
class B extends A {}
void main() {
foo(B());
}
This program is accepted by the analyzer as well as the common front end, and it throws at run time because the given instance of B
does not have an implementation of _private
.
I have argued (e.g., here) that this situation should be subject to a warning (or an error). However, the counterargument has been raised (e.g., here) that it is more important to avoid that the addition of a private declaration causes code in other libraries to fail.
// --- Library 'n045lib.dart', version 1.
abstract class A {}
void foo(A a) {}
// --- Library 'n045lib.dart', version 2.
abstract class A {
int get _private;
}
void foo(A a) {}
// --- Library 'n045lib.dart', version 3 again.
abstract class A {
int get _private;
}
void foo(A a) => print(a._private);
Surely, B
should not be an error with version 1. According to the "addition of private members is non-breaking" principle, B
should not be an error with version 2, either. Surely, then, version 3 cannot make B
an error.
So we accept that B
does not have an implementation of _private
because it would be worse to report an error for B
based on such updates of the library that declares A
.
However, if we maintain that the addition of a private member should not cause an error to occur in a different library then we should also accept the following program with no compile-time errors:
// --- Library 'n046lib.dart', version 1.
class A {}
abstract class B extends A {
int _private([int _]);
}
void foo(B b) => print(b._private());
// --- Library 'n046lib.dart', version 2.
class A {
int _private() => 0;
}
abstract class B extends A {
int _private([int _]);
}
void foo(B b) => print(b._private());
// --- Library 'n046.dart'.
import 'n046lib.dart';
class C extends B {}
void main() {
foo(C());
}
The analyzer again accepts both versions of the code, but the common front end reports an error for version 2, where we have added the private declaration A._private
:
n046.dart:3:7: Error: The implementation of '_private' in the non-abstract class 'C' does not conform to its interface.
class C extends B {}
^
n046lib.dart:2:7: Context: The method 'A._private' has fewer positional arguments than those of overridden method 'B._private'.
int _private() => 0;
^
n046lib.dart:6:7: Context: This is the overridden method ('_private').
int _private([int _]);
^
Again, we'd incur a run-time failure because the instance of C
does not have an implementation of _private
(and the one which is declared in A
can not be used because it does not have the required signature). We aren't able to run it, though, because of the error.
We should at least address the discrepancy such that all tools will agree on the existence/non-existence of a compile-time error.
- Option 1: Make CFE stop reporting this error, it is always OK for a class declared in a library L to have no implementation for some private members in a different library L2.
- Option 2: Drop the principle about "adding a private member doesn't break code in other libraries" and report the error in all tools (this would affect both 'n045.dart' and 'n046.dart').
@dart-lang/language-team, WDYT?