From 87ccae9e3f31873d67eb09cebaf2666f4edc16af Mon Sep 17 00:00:00 2001 From: Erik Ernst Date: Wed, 1 Sep 2021 23:47:47 +0000 Subject: [PATCH] Add tests about covariant parameters and inherited methods Change-Id: Ibf2e4256605c1c479c6b244c44c1c424b0a2e6ca Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/208504 Reviewed-by: Nate Bosch Commit-Queue: Erik Ernst --- .../override_covariant_both_test.dart | 508 +++++++++++++++++ .../override_covariant_class_test.dart | 525 ++++++++++++++++++ .../override_covariant_interface_test.dart | 511 +++++++++++++++++ .../override_covariant_superclass_test.dart | 508 +++++++++++++++++ 4 files changed, 2052 insertions(+) create mode 100644 tests/language/covariant/override_covariant_both_test.dart create mode 100644 tests/language/covariant/override_covariant_class_test.dart create mode 100644 tests/language/covariant/override_covariant_interface_test.dart create mode 100644 tests/language/covariant/override_covariant_superclass_test.dart diff --git a/tests/language/covariant/override_covariant_both_test.dart b/tests/language/covariant/override_covariant_both_test.dart new file mode 100644 index 00000000000..0a381c73de7 --- /dev/null +++ b/tests/language/covariant/override_covariant_both_test.dart @@ -0,0 +1,508 @@ +// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +// Test cases where an inherited method has a parameter `p0` that +// corresponds to a parameter `p1` in the interface, where `p0` and `p1` are +// both covariant-by-declaration. +// +// The library contains groups of classes depending on each other, and there +// is no connection between the groups. Each group has a number (0 .. 17) +// which is used at the end of most names in the group. For example, the first +// group consists of the classes `B0`, `C0`, `DD0`, and `D0`. The group number +// is shown as `#` in the remainder of this comment. +// +// In every group, the classes `C#` and `D#` are concrete leaf classes +// exhibiting the situation of interest, that is, an inherited implementation +// of the method `m` as well as a signature for `m` in the interface of the +// class, with a parameter that has the above-mentioned properties. +// +// The parameter type in the implementation and the parameter type in the +// method signature is (`num`, `num`), (`int`, `num`) and (`num`, `int`), +// respectively, such that we test a covariant, contravariant and invariant +// parameter type relationship in the overrides. +// +// In every group, the class `B#` contains the implementation of the method +// `m` which is inherited. Class `A#` and mixin `M#`, present in some groups, +// are used to provide the method implementation to `B#` using a mixin +// application. +// +// The classes `BB#`, `I#`, `DD#`, `J#`, and `JJ#` are helper classes for `C#` +// and `#D`. +// +// Class `BB#` is a subclass of `B#` and a superclass of `C#` and `D#`; it +// introduces the method signature of `m` into the superclass chain. +// +// Class `DD#` is implemented by `D#`. Classes `I#`, `J#`, and `JJ#` are +// implemented by `C#` or `D#`. Class `DD#` and `JJ#` ensure that `m` can be +// called with an argument of type `Object`. Class `I#` and `J#` introduce the +// method signature to `C#` or `D#` via an `implements` relation. +// +// Finally, `main` contains test cases where the dynamic type check is +// incurred, using variable types and arguments that make the type check +// fail whenever possible. +// +// The comments below about 'Superclass' and 'interface' indicate the way in +// which the superclass of `C#` and `D#` is structured (it can be a regular +// class or a mixin), and the way in which the superinterface that provides +// the method signature is structured (it can be declared in `C#` or `D#`, +// "immediate", or it can be obtained via an `extends` relation or via an +// `implements` relation). + +import 'package:expect/expect.dart'; + +// Superclass: regular, interface: immediate. + +class B0 { + void m(covariant num x) {} +} + +class C0 extends B0 { + void m(covariant num x); +} + +abstract class DD0 { + void m(covariant Object x); +} + +class D0 extends B0 implements DD0 { + void m(num x); +} + +class B1 { + void m(covariant int x) {} +} + +class C1 extends B1 { + void m(covariant num x); +} + +abstract class DD1 { + void m(covariant Object x); +} + +class D1 extends B1 implements DD1 { + void m(num x); +} + +class B2 { + void m(covariant num x) {} +} + +class C2 extends B2 { + void m(covariant int x); +} + +abstract class DD2 { + void m(covariant Object x); +} + +class D2 extends B2 implements DD2 { + void m(int x); +} + +// Superclass: regular, interface: extends. + +class B3 { + void m(covariant num x) {} +} + +abstract class BB3 extends B3 { + void m(covariant num x); +} + +class C3 extends BB3 {} + +abstract class DD3 { + void m(covariant Object x); +} + +class D3 extends BB3 implements DD3 {} + +class B4 { + void m(covariant int x) {} +} + +abstract class BB4 extends B4 { + void m(covariant num x); +} + +class C4 extends BB4 {} + +abstract class DD4 { + void m(covariant Object x); +} + +class D4 extends BB4 implements DD4 {} + +class B5 { + void m(covariant num x) {} +} + +abstract class BB5 extends B5 { + void m(covariant int x); +} + +class C5 extends BB5 {} + +abstract class DD5 { + void m(covariant Object x); +} + +class D5 extends BB5 implements DD5 {} + +// Superclass: regular, interface: implements. + +class B6 { + void m(covariant num x) {} +} + +abstract class I6 { + void m(covariant num x); +} + +class C6 extends B6 implements I6 {} + +abstract class JJ6 { + void m(covariant Object x); +} + +abstract class J6 { + void m(num x); +} + +class D6 extends B6 implements J6, JJ6 {} + +class B7 { + void m(covariant int x) {} +} + +abstract class I7 { + void m(covariant num x); +} + +class C7 extends B7 implements I7 {} + +abstract class JJ7 { + void m(covariant Object x); +} + +abstract class J7 { + void m(num x); +} + +class D7 extends B7 implements J7, JJ7 {} + +class B8 { + void m(covariant num x) {} +} + +abstract class I8 { + void m(covariant int x); +} + +class C8 extends B8 implements I8 {} + +abstract class JJ8 { + void m(covariant Object x); +} + +abstract class J8 { + void m(int x); +} + +class D8 extends B8 implements J8, JJ8 {} + +// Superclass: mixed-in, interface: immediate. + +class A9 {} + +mixin M9 { + void m(covariant num x) {} +} + +class B9 extends A9 with M9 {} + +class C9 extends B9 { + void m(covariant num x); +} + +abstract class DD9 { + void m(covariant Object x); +} + +class D9 extends B9 implements DD9 { + void m(num x); +} + +class A10 {} + +mixin M10 { + void m(covariant int x) {} +} + +class B10 extends A10 with M10 {} + +class C10 extends B10 { + void m(covariant num x); +} + +abstract class DD10 { + void m(covariant Object x); +} + +class D10 extends B10 implements DD10 { + void m(num x); +} + +class A11 {} + +mixin M11 { + void m(covariant num x) {} +} + +class B11 extends A11 with M11 {} + +class C11 extends B11 { + void m(covariant int x); +} + +abstract class DD11 { + void m(covariant Object x); +} + +class D11 extends B11 implements DD11 { + void m(int x); +} + +// Superclass: mixed-in, interface: extends. + +class A12 {} + +mixin M12 { + void m(covariant num x) {} +} + +class B12 extends A12 with M12 {} + +abstract class BB12 extends B12 { + void m(covariant num x); +} + +class C12 extends BB12 {} + +abstract class DD12 { + void m(covariant Object x); +} + +class D12 extends BB12 implements DD12 {} + +class A13 {} + +mixin M13 { + void m(covariant int x) {} +} + +class B13 extends A13 with M13 {} + +abstract class BB13 extends B13 { + void m(covariant num x); +} + +class C13 extends BB13 {} + +abstract class DD13 { + void m(covariant Object x); +} + +class D13 extends BB13 implements DD13 {} + +class A14 {} + +mixin M14 { + void m(covariant num x) {} +} + +class B14 extends A14 with M14 {} + +abstract class BB14 extends B14 { + void m(covariant int x); +} + +class C14 extends BB14 {} + +abstract class DD14 { + void m(covariant Object x); +} + +class D14 extends BB14 implements DD14 {} + +// Superclass: mixed-in, interface: implements. + +class A15 {} + +mixin M15 { + void m(covariant num x) {} +} + +class B15 extends A15 with M15 {} + +abstract class I15 { + void m(covariant num x); +} + +class C15 extends B15 implements I15 {} + +abstract class JJ15 { + void m(covariant Object x); +} + +abstract class J15 { + void m(num x); +} + +class D15 extends B15 implements J15, JJ15 {} + +class A16 {} + +mixin M16 { + void m(covariant int x) {} +} + +class B16 extends A16 with M16 {} + +abstract class I16 { + void m(covariant num x); +} + +class C16 extends B16 implements I16 {} + +abstract class JJ16 { + void m(covariant Object x); +} + +abstract class J16 { + void m(num x); +} + +class D16 extends B16 implements J16, JJ16 {} + +class A17 {} + +mixin M17 { + void m(covariant num x) {} +} + +class B17 extends A17 with M17 {} + +abstract class I17 { + void m(covariant int x); +} + +class C17 extends B17 implements I17 {} + +abstract class JJ17 { + void m(covariant Object x); +} + +abstract class J17 { + void m(int x); +} + +class D17 extends B17 implements J17, JJ17 {} + +void main() { + // Demonstrate that each leaf class requires and has a dynamic + // type check (which is most likely placed in a forwarding stub). + const o = Object(); + + B0 x0 = C0(); + x0.m(0.0); + DD0 y0 = D0(); + Expect.throws(() => y0.m(o)); + + C1 x1 = C1(); + Expect.throws(() => x1.m(1.1)); + DD1 y1 = D1(); + Expect.throws(() => y1.m(o)); + + B2 x2 = C2(); + x2.m(2.2); + DD2 y2 = D2(); + Expect.throws(() => y2.m(o)); + + B3 x3 = C3(); + x3.m(3.3); + DD3 y3 = D3(); + Expect.throws(() => y3.m(o)); + + C4 x4 = C4(); + Expect.throws(() => x4.m(4.4)); + DD4 y4 = D4(); + Expect.throws(() => y4.m(o)); + + B5 x5 = C5(); + x5.m(5.5); + DD5 y5 = D5(); + Expect.throws(() => y5.m(o)); + + B6 x6 = C6(); + x6.m(6.6); + JJ6 y6 = D6(); + Expect.throws(() => y6.m(o)); + + C7 x7 = C7(); + Expect.throws(() => x7.m(7.7)); + JJ7 y7 = D7(); + Expect.throws(() => y7.m(o)); + + B8 x8 = C8(); + x8.m(8.8); + JJ8 y8 = D8(); + Expect.throws(() => y8.m(o)); + + B9 x9 = C9(); + x9.m(1.1); + DD9 y9 = D9(); + Expect.throws(() => y9.m(o)); + + C10 x10 = C10(); + Expect.throws(() => x10.m(10.10)); + DD10 y10 = D10(); + Expect.throws(() => y10.m(o)); + + B11 x11 = C11(); + x11.m(11.11); + DD11 y11 = D11(); + Expect.throws(() => y11.m(o)); + + B12 x12 = C12(); + x12.m(12.12); + DD12 y12 = D12(); + Expect.throws(() => y12.m(o)); + + C13 x13 = C13(); + Expect.throws(() => x13.m(13.13)); + DD13 y13 = D13(); + Expect.throws(() => y13.m(o)); + + B14 x14 = C14(); + x14.m(14.14); + DD14 y14 = D14(); + Expect.throws(() => y14.m(o)); + + B15 x15 = C15(); + x15.m(15.15); + JJ15 y15 = D15(); + Expect.throws(() => y15.m(o)); + + C16 x16 = C16(); + Expect.throws(() => x16.m(16.16)); + JJ16 y16 = D16(); + Expect.throws(() => y16.m(o)); + + B17 x17 = C17(); + x17.m(17.17); + JJ17 y17 = D17(); + Expect.throws(() => y17.m(o)); +} diff --git a/tests/language/covariant/override_covariant_class_test.dart b/tests/language/covariant/override_covariant_class_test.dart new file mode 100644 index 00000000000..7de1b69419c --- /dev/null +++ b/tests/language/covariant/override_covariant_class_test.dart @@ -0,0 +1,525 @@ +// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +// Test error detection where an inherited method has a parameter `p0` +// that corresponds to a parameter `p1` in the interface, where `p1` is +// covariant-by-class and -by-declaration where needed, but `p0` is not. +// +// The library contains groups of classes depending on each other, and there +// is no connection between the groups. Each group has a number (0 .. 17) +// which is used at the end of most names in the group. For example, the first +// group consists of the classes `B0`, `C0`, `DD0`, and `D0`. The group number +// is shown as `#` in the remainder of this comment. +// +// In every group, the classes `C#` and `D#` are concrete leaf classes +// exhibiting the situation of interest, that is, an inherited implementation +// of the method `m` as well as a signature for `m` in the interface of the +// class, with a parameter that has the above-mentioned properties. +// +// The parameter type in the implementation and the parameter type in the +// method signature is (`num`, `num`), (`int`, `num`) and (`num`, `int`), +// respectively, such that we test a covariant, contravariant and invariant +// parameter type relationship in the overrides. +// +// In every group, the class `B#` contains the implementation of the method +// `m` which is inherited. Class `A#` and mixin `M#`, present in some groups, +// are used to provide the method implementation to `B#` using a mixin +// application. +// +// The classes `BB#`, `I#`, `DD#`, `J#`, and `JJ#` are helper classes for `C#` +// and `#D`. +// +// Class `BB#` is a subclass of `B#` and a superclass of `C#` and `D#`; it +// introduces the method signature of `m` into the superclass chain. +// +// Class `DD#` is implemented by `D#`. Classes `I#`, `J#`, and `JJ#` are +// implemented by `C#` or `D#`. Class `DD#` and `JJ#` ensure that `m` can be +// called with an argument of type `Object`. Class `I#` and `J#` introduce the +// method signature to `C#` or `D#` via an `implements` relation. +// +// Finally, `main` contains test cases where the dynamic type check is +// incurred, using variable types and arguments that make the type check +// fail whenever possible. +// +// The comments below about 'Superclass' and 'interface' indicate the way in +// which the superclass of `C#` and `D#` is structured (it can be a regular +// class or a mixin), and the way in which the superinterface that provides +// the method signature is structured (it can be declared in `C#` or `D#`, +// "immediate", or it can be obtained via an `extends` relation or via an +// `implements` relation). + +import 'package:expect/expect.dart'; + +// Superclass: regular, interface: immediate. + +class B0 { + void m(num x) {} +} + +abstract class CC0 extends B0 { + void m(covariant X x); +} + +class C0 extends CC0 {} + +abstract class DD0 { + void m(X x); +} + +class D0 extends B0 implements DD0 { + void m(num x); +} + +// Following the pattern, the classes `B1` .. `D1` are as shown below. They +// are commented out, because the declaration `CC1.m` is an error, because +// its parameter has a type which is neither a subtype nor a supertype of +// the parameter type in `B1.m`. It is not useful to change the bound of `X` +// in `CC1` to `int`, because that yields a set of classes with exactly the +// same properties as those of `B0` .. `D0` (just change `num` to `int` +// everywhere). +// +// class B1 { +// void m(int x) {} +// } +// +// abstract class CC1 extends B1 { +// void m(covariant X x); +// } +// +// class C1 extends CC1 {} +// +// abstract class DD1 { +// void m(X x); +// } +// +// class D1 extends B1 implements DD1 { +// void m(int x); +// } + +class B2 { + void m(num x) {} +} + +abstract class CC2 extends B2 { + void m(covariant X x); +} + +class C2 extends CC2 {} + +abstract class DD2 { + void m(X x); +} + +class D2 extends B2 implements DD2 { + void m(num x); +} + +// Superclass: regular, interface: extends. + +class B3 { + void m(num x) {} +} + +abstract class BB3 extends B3 { + void m(covariant X x); +} + +class C3 extends BB3 {} + +abstract class DD3 { + void m(X x); +} + +class D3 extends BB3 implements DD3 {} + +class B4 { + void m(int x) {} +} + +abstract class BB4 extends B4 { + void m(covariant X x); +} + +class C4 extends BB4 {} + +abstract class DD4 { + void m(X x); +} + +class D4 extends BB4 implements DD4 {} + +class B5 { + void m(num x) {} +} + +abstract class BB5 extends B5 { + void m(covariant X x); +} + +class C5 extends BB5 {} + +abstract class DD5 { + void m(X x); +} + +class D5 extends BB5 implements DD5 {} + +// Superclass: regular, interface: implements. + +class B6 { + void m(num x) {} +} + +abstract class I6 { + void m(X x); +} + +class C6 extends B6 implements I6 {} + +abstract class JJ6 { + void m(Object x); +} + +abstract class J6 implements JJ6 { + void m(covariant X x); +} + +class D6 extends B6 implements J6 {} + +class B7 { + void m(int x) {} +} + +abstract class I7 { + void m(X x); +} + +class C7 extends B7 implements I7 {} + +abstract class JJ7 { + void m(Object x); +} + +abstract class J7 implements JJ7 { + void m(covariant X x); +} + +class D7 extends B7 implements J7 {} + +class B8 { + void m(num x) {} +} + +abstract class I8 { + void m(X x); +} + +class C8 extends B8 implements I8 {} + +abstract class JJ8 { + void m(Object x); +} + +abstract class J8 implements JJ8 { + void m(covariant X x); +} + +class D8 extends B8 implements J8 {} + +// Superclass: mixed-in, interface: immediate. + +class A9 {} + +mixin M9 { + void m(num x) {} +} + +class B9 extends A9 with M9 {} + +abstract class CC9 extends B9 { + void m(covariant X x); +} + +class C9 extends CC9 {} + +abstract class DD9 { + void m(X x); +} + +class D9 extends B9 implements DD9 { + void m(num x); +} + +class A10 {} + +mixin M10 { + void m(int x) {} +} + +class B10 extends A10 with M10 {} + +abstract class CC10 extends B10 { + void m(covariant X x); +} + +class C10 extends CC10 {} + +abstract class DD10 { + void m(X x); +} + +class D10 extends B10 implements DD10 { + void m(int x); +} + +class A11 {} + +mixin M11 { + void m(num x) {} +} + +class B11 extends A11 with M11 {} + +abstract class CC11 extends B11 { + void m(covariant X x); +} + +class C11 extends CC11 {} + +abstract class DD11 { + void m(X x); +} + +class D11 extends B11 implements DD11 { + void m(num x); +} + +// Superclass: mixed-in, interface: extends. + +class A12 {} + +mixin M12 { + void m(num x) {} +} + +class B12 extends A12 with M12 {} + +abstract class BB12 extends B12 { + void m(covariant X x); +} + +class C12 extends BB12 {} + +abstract class DD12 { + void m(X x); +} + +class D12 extends BB12 implements DD12 {} + +class A13 {} + +mixin M13 { + void m(int x) {} +} + +class B13 extends A13 with M13 {} + +abstract class BB13 extends B13 { + void m(covariant X x); +} + +class C13 extends BB13 {} + +abstract class DD13 { + void m(X x); +} + +class D13 extends BB13 implements DD13 {} + +class A14 {} + +mixin M14 { + void m(num x) {} +} + +class B14 extends A14 with M14 {} + +abstract class BB14 extends B14 { + void m(covariant X x); +} + +class C14 extends BB14 {} + +abstract class DD14 { + void m(X x); +} + +class D14 extends BB14 implements DD14 {} + +// Superclass: mixed-in, interface: implements. + +class A15 {} + +mixin M15 { + void m(num x) {} +} + +class B15 extends A15 with M15 {} + +abstract class I15 { + void m(X x); +} + +class C15 extends B15 implements I15 {} + +abstract class JJ15 { + void m(Object x); +} + +abstract class J15 implements JJ15 { + void m(covariant X x); +} + +class D15 extends B15 implements J15 {} + +class A16 {} + +mixin M16 { + void m(int x) {} +} + +class B16 extends A16 with M16 {} + +abstract class I16 { + void m(X x); +} + +class C16 extends B16 implements I16 {} + +abstract class JJ16 { + void m(Object x); +} + +abstract class J16 implements JJ16 { + void m(covariant X x); +} + +class D16 extends B16 implements J16 {} + +class A17 {} + +mixin M17 { + void m(num x) {} +} + +class B17 extends A17 with M17 {} + +abstract class I17 { + void m(X x); +} + +class C17 extends B17 implements I17 {} + +abstract class JJ17 { + void m(Object x); +} + +abstract class J17 implements JJ17 { + void m(covariant X x); +} + +class D17 extends B17 implements J17 {} + +void main() { + // Demonstrate that each leaf class requires and has a dynamic + // type check (which is most likely placed in a forwarding stub). + const o = Object(); + + CC0 x0 = C0(); + Expect.throws(() => x0.m(o)); + DD0 y0 = D0(); + Expect.throws(() => y0.m(o)); + + // Group 1 was eliminated, see comment containing `class B1`. + + CC2 x2 = C2(); + Expect.throws(() => x2.m(o)); + DD2 y2 = D2(); + Expect.throws(() => y2.m(o)); + + BB3 x3 = C3(); + Expect.throws(() => x3.m(o)); + DD3 y3 = D3(); + Expect.throws(() => y3.m(o)); + + BB4 x4 = C4(); + Expect.throws(() => x4.m(o)); + DD4 y4 = D4(); + Expect.throws(() => y4.m(o)); + + BB5 x5 = C5(); + Expect.throws(() => x5.m(o)); + DD5 y5 = D5(); + Expect.throws(() => y5.m(o)); + + I6 x6 = C6(); + Expect.throws(() => x6.m(o)); + JJ6 y6 = D6(); + Expect.throws(() => y6.m(o)); + + I7 x7 = C7(); + Expect.throws(() => x7.m(o)); + JJ7 y7 = D7(); + Expect.throws(() => y7.m(o)); + + I8 x8 = C8(); + Expect.throws(() => x8.m(o)); + JJ8 y8 = D8(); + Expect.throws(() => y8.m(o)); + + CC9 x9 = C9(); + Expect.throws(() => x9.m(o)); + DD9 y9 = D9(); + Expect.throws(() => y9.m(o)); + + CC10 x10 = C10(); + Expect.throws(() => x10.m(o)); + DD10 y10 = D10(); + Expect.throws(() => y10.m(o)); + + CC11 x11 = C11(); + Expect.throws(() => x11.m(o)); + DD11 y11 = D11(); + Expect.throws(() => y11.m(o)); + + BB12 x12 = C12(); + Expect.throws(() => x12.m(o)); + DD12 y12 = D12(); + Expect.throws(() => y12.m(o)); + + BB13 x13 = C13(); + Expect.throws(() => x13.m(o)); + DD13 y13 = D13(); + Expect.throws(() => y13.m(o)); + + BB14 x14 = C14(); + Expect.throws(() => x14.m(o)); + DD14 y14 = D14(); + Expect.throws(() => y14.m(o)); + + I15 x15 = C15(); + Expect.throws(() => x15.m(o)); + JJ15 y15 = D15(); + Expect.throws(() => y15.m(o)); + + I16 x16 = C16(); + Expect.throws(() => x16.m(o)); + JJ16 y16 = D16(); + Expect.throws(() => y16.m(o)); + + I17 x17 = C17(); + Expect.throws(() => x17.m(o)); + JJ17 y17 = D17(); + Expect.throws(() => y17.m(o)); +} diff --git a/tests/language/covariant/override_covariant_interface_test.dart b/tests/language/covariant/override_covariant_interface_test.dart new file mode 100644 index 00000000000..c0fdafb3da3 --- /dev/null +++ b/tests/language/covariant/override_covariant_interface_test.dart @@ -0,0 +1,511 @@ +// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +// Test the handling of a situation where an inherited method has a parameter +// `p0` that corresponds to a parameter `p1` in the interface, where `p1` +// is covariant-by-declaration, but `p0` is not. In this case, the inherited +// method may need to be overridden by an implicitly induced stub, because +// it must perform a dynamic type check on its parameter, and the inherited +// method doesn't do that. +// +// The library contains groups of classes depending on each other, and there +// is no connection between the groups. Each group has a number (0 .. 17) +// which is used at the end of most names in the group. For example, the first +// group consists of the classes `B0`, `C0`, `DD0`, and `D0`. The group number +// is shown as `#` in the remainder of this comment. +// +// In every group, the classes `C#` and `D#` are concrete leaf classes +// exhibiting the situation of interest, that is, an inherited implementation +// of the method `m` as well as a signature for `m` in the interface of the +// class, with a parameter that has the above-mentioned properties. +// +// The parameter type in the implementation and the parameter type in the +// method signature is (`num`, `num`), (`int`, `num`) and (`num`, `int`), +// respectively, such that we test a covariant, contravariant and invariant +// parameter type relationship in the overrides. +// +// In every group, the class `B#` contains the implementation of the method +// `m` which is inherited. Class `A#` and mixin `M#`, present in some groups, +// are used to provide the method implementation to `B#` using a mixin +// application. +// +// The classes `BB#`, `I#`, `DD#`, `J#`, and `JJ#` are helper classes for `C#` +// and `#D`. +// +// Class `BB#` is a subclass of `B#` and a superclass of `C#` and `D#`; it +// introduces the method signature of `m` into the superclass chain. +// +// Class `DD#` is implemented by `D#`. Classes `I#`, `J#`, and `JJ#` are +// implemented by `C#` or `D#`. Class `DD#` and `JJ#` ensure that `m` can be +// called with an argument of type `Object`. Class `I#` and `J#` introduce the +// method signature to `C#` or `D#` via an `implements` relation. +// +// Finally, `main` contains test cases where the dynamic type check is +// incurred, using variable types and arguments that make the type check +// fail whenever possible. +// +// The comments below about 'Superclass' and 'interface' indicate the way in +// which the superclass of `C#` and `D#` is structured (it can be a regular +// class or a mixin), and the way in which the superinterface that provides +// the method signature is structured (it can be declared in `C#` or `D#`, +// "immediate", or it can be obtained via an `extends` relation or via an +// `implements` relation). + +import 'package:expect/expect.dart'; + +// Superclass: regular, interface: immediate. + +class B0 { + void m(num x) {} +} + +class C0 extends B0 { + void m(covariant num x); +} + +abstract class DD0 { + void m(Object x); +} + +class D0 extends B0 implements DD0 { + void m(covariant num x); +} + +class B1 { + void m(int x) {} +} + +class C1 extends B1 { + void m(covariant num x); +} + +abstract class DD1 { + void m(Object x); +} + +class D1 extends B1 implements DD1 { + void m(covariant num x); +} + +class B2 { + void m(num x) {} +} + +class C2 extends B2 { + void m(covariant int x); +} + +abstract class DD2 { + void m(Object x); +} + +class D2 extends B2 implements DD2 { + void m(covariant int x); +} + +// Superclass: regular, interface: extends. + +class B3 { + void m(num x) {} +} + +abstract class BB3 extends B3 { + void m(covariant num x); +} + +class C3 extends BB3 {} + +abstract class DD3 { + void m(Object x); +} + +class D3 extends BB3 implements DD3 {} + +class B4 { + void m(int x) {} +} + +abstract class BB4 extends B4 { + void m(covariant num x); +} + +class C4 extends BB4 {} + +abstract class DD4 { + void m(Object x); +} + +class D4 extends BB4 implements DD4 {} + +class B5 { + void m(num x) {} +} + +abstract class BB5 extends B5 { + void m(covariant int x); +} + +class C5 extends BB5 {} + +abstract class DD5 { + void m(Object x); +} + +class D5 extends BB5 implements DD5 {} + +// Superclass: regular, interface: implements. + +class B6 { + void m(num x) {} +} + +abstract class I6 { + void m(covariant num x); +} + +class C6 extends B6 implements I6 {} + +abstract class JJ6 { + void m(Object x); +} + +abstract class J6 implements JJ6 { + void m(covariant num x); +} + +class D6 extends B6 implements J6 {} + +class B7 { + void m(int x) {} +} + +abstract class I7 { + void m(covariant num x); +} + +class C7 extends B7 implements I7 {} + +abstract class JJ7 { + void m(Object x); +} + +abstract class J7 implements JJ7 { + void m(covariant num x); +} + +class D7 extends B7 implements J7 {} + +class B8 { + void m(num x) {} +} + +abstract class I8 { + void m(covariant int x); +} + +class C8 extends B8 implements I8 {} + +abstract class JJ8 { + void m(Object x); +} + +abstract class J8 implements JJ8 { + void m(covariant int x); +} + +class D8 extends B8 implements J8 {} + +// Superclass: mixed-in, interface: immediate. + +class A9 {} + +mixin M9 { + void m(num x) {} +} + +class B9 extends A9 with M9 {} + +class C9 extends B9 { + void m(covariant num x); +} + +abstract class DD9 { + void m(Object x); +} + +class D9 extends B9 implements DD9 { + void m(covariant num x); +} + +class A10 {} + +mixin M10 { + void m(int x) {} +} + +class B10 extends A10 with M10 {} + +class C10 extends B10 { + void m(covariant num x); +} + +abstract class DD10 { + void m(Object x); +} + +class D10 extends B10 implements DD10 { + void m(covariant num x); +} + +class A11 {} + +mixin M11 { + void m(num x) {} +} + +class B11 extends A11 with M11 {} + +class C11 extends B11 { + void m(covariant int x); +} + +abstract class DD11 { + void m(Object x); +} + +class D11 extends B11 implements DD11 { + void m(covariant int x); +} + +// Superclass: mixed-in, interface: extends. + +class A12 {} + +mixin M12 { + void m(num x) {} +} + +class B12 extends A12 with M12 {} + +abstract class BB12 extends B12 { + void m(covariant num x); +} + +class C12 extends BB12 {} + +abstract class DD12 { + void m(Object x); +} + +class D12 extends BB12 implements DD12 {} + +class A13 {} + +mixin M13 { + void m(int x) {} +} + +class B13 extends A13 with M13 {} + +abstract class BB13 extends B13 { + void m(covariant num x); +} + +class C13 extends BB13 {} + +abstract class DD13 { + void m(Object x); +} + +class D13 extends BB13 implements DD13 {} + +class A14 {} + +mixin M14 { + void m(num x) {} +} + +class B14 extends A14 with M14 {} + +abstract class BB14 extends B14 { + void m(covariant int x); +} + +class C14 extends BB14 {} + +abstract class DD14 { + void m(Object x); +} + +class D14 extends BB14 implements DD14 {} + +// Superclass: mixed-in, interface: implements. + +class A15 {} + +mixin M15 { + void m(num x) {} +} + +class B15 extends A15 with M15 {} + +abstract class I15 { + void m(covariant num x); +} + +class C15 extends B15 implements I15 {} + +abstract class JJ15 { + void m(Object x); +} + +abstract class J15 implements JJ15 { + void m(covariant num x); +} + +class D15 extends B15 implements J15 {} + +class A16 {} + +mixin M16 { + void m(int x) {} +} + +class B16 extends A16 with M16 {} + +abstract class I16 { + void m(covariant num x); +} + +class C16 extends B16 implements I16 {} + +abstract class JJ16 { + void m(Object x); +} + +abstract class J16 implements JJ16 { + void m(covariant num x); +} + +class D16 extends B16 implements J16 {} + +class A17 {} + +mixin M17 { + void m(num x) {} +} + +class B17 extends A17 with M17 {} + +abstract class I17 { + void m(covariant int x); +} + +class C17 extends B17 implements I17 {} + +abstract class JJ17 { + void m(Object x); +} + +abstract class J17 implements JJ17 { + void m(covariant int x); +} + +class D17 extends B17 implements J17 {} + +void main() { + // Demonstrate that each leaf class requires and has a dynamic + // type check (which is most likely placed in a forwarding stub). + const o = Object(); + + B0 x0 = C0(); + x0.m(0.0); + DD0 y0 = D0(); + Expect.throws(() => y0.m(o)); + + C1 x1 = C1(); + Expect.throws(() => x1.m(1.1)); + DD1 y1 = D1(); + Expect.throws(() => y1.m(o)); + + B2 x2 = C2(); + x2.m(2.2); + DD2 y2 = D2(); + Expect.throws(() => y2.m(o)); + + B3 x3 = C3(); + x3.m(3.3); + DD3 y3 = D3(); + Expect.throws(() => y3.m(o)); + + C4 x4 = C4(); + Expect.throws(() => x4.m(4.4)); + DD4 y4 = D4(); + Expect.throws(() => y4.m(o)); + + B5 x5 = C5(); + x5.m(5.5); + DD5 y5 = D5(); + Expect.throws(() => y5.m(o)); + + B6 x6 = C6(); + x6.m(6.6); + JJ6 y6 = D6(); + Expect.throws(() => y6.m(o)); + + C7 x7 = C7(); + Expect.throws(() => x7.m(7.7)); + JJ7 y7 = D7(); + Expect.throws(() => y7.m(o)); + + B8 x8 = C8(); + x8.m(8.8); + JJ8 y8 = D8(); + Expect.throws(() => y8.m(o)); + + B9 x9 = C9(); + x9.m(1.1); + DD9 y9 = D9(); + Expect.throws(() => y9.m(o)); + + C10 x10 = C10(); + Expect.throws(() => x10.m(10.10)); + DD10 y10 = D10(); + Expect.throws(() => y10.m(o)); + + B11 x11 = C11(); + x11.m(11.11); + DD11 y11 = D11(); + Expect.throws(() => y11.m(o)); + + B12 x12 = C12(); + x12.m(12.12); + DD12 y12 = D12(); + Expect.throws(() => y12.m(o)); + + C13 x13 = C13(); + Expect.throws(() => x13.m(13.13)); + DD13 y13 = D13(); + Expect.throws(() => y13.m(o)); + + B14 x14 = C14(); + x14.m(14.14); + DD14 y14 = D14(); + Expect.throws(() => y14.m(o)); + + B15 x15 = C15(); + x15.m(15.15); + JJ15 y15 = D15(); + Expect.throws(() => y15.m(o)); + + C16 x16 = C16(); + Expect.throws(() => x16.m(16.16)); + JJ16 y16 = D16(); + Expect.throws(() => y16.m(o)); + + B17 x17 = C17(); + x17.m(17.17); + JJ17 y17 = D17(); + Expect.throws(() => y17.m(o)); +} diff --git a/tests/language/covariant/override_covariant_superclass_test.dart b/tests/language/covariant/override_covariant_superclass_test.dart new file mode 100644 index 00000000000..5c78272ab8f --- /dev/null +++ b/tests/language/covariant/override_covariant_superclass_test.dart @@ -0,0 +1,508 @@ +// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +// Test cases where an inherited method has a parameter `p0` that +// corresponds to a parameter `p1` in the interface, where `p0` is +// covariant-by-declaration, but `p1` is not. +// +// The library contains groups of classes depending on each other, and there +// is no connection between the groups. Each group has a number (0 .. 17) +// which is used at the end of most names in the group. For example, the first +// group consists of the classes `B0`, `C0`, `DD0`, and `D0`. The group number +// is shown as `#` in the remainder of this comment. +// +// In every group, the classes `C#` and `D#` are concrete leaf classes +// exhibiting the situation of interest, that is, an inherited implementation +// of the method `m` as well as a signature for `m` in the interface of the +// class, with a parameter that has the above-mentioned properties. +// +// The parameter type in the implementation and the parameter type in the +// method signature is (`num`, `num`), (`int`, `num`) and (`num`, `int`), +// respectively, such that we test a covariant, contravariant and invariant +// parameter type relationship in the overrides. +// +// In every group, the class `B#` contains the implementation of the method +// `m` which is inherited. Class `A#` and mixin `M#`, present in some groups, +// are used to provide the method implementation to `B#` using a mixin +// application. +// +// The classes `BB#`, `I#`, `DD#`, `J#`, and `JJ#` are helper classes for `C#` +// and `#D`. +// +// Class `BB#` is a subclass of `B#` and a superclass of `C#` and `D#`; it +// introduces the method signature of `m` into the superclass chain. +// +// Class `DD#` is implemented by `D#`. Classes `I#`, `J#`, and `JJ#` are +// implemented by `C#` or `D#`. Class `DD#` and `JJ#` ensure that `m` can be +// called with an argument of type `Object`. Class `I#` and `J#` introduce the +// method signature to `C#` or `D#` via an `implements` relation. +// +// Finally, `main` contains test cases where the dynamic type check is +// incurred, using variable types and arguments that make the type check +// fail whenever possible. +// +// The comments below about 'Superclass' and 'interface' indicate the way in +// which the superclass of `C#` and `D#` is structured (it can be a regular +// class or a mixin), and the way in which the superinterface that provides +// the method signature is structured (it can be declared in `C#` or `D#`, +// "immediate", or it can be obtained via an `extends` relation or via an +// `implements` relation). + +import 'package:expect/expect.dart'; + +// Superclass: regular, interface: immediate. + +class B0 { + void m(covariant num x) {} +} + +class C0 extends B0 { + void m(num x); +} + +abstract class DD0 { + void m(Object x); +} + +class D0 extends B0 implements DD0 { + void m(num x); +} + +class B1 { + void m(covariant int x) {} +} + +class C1 extends B1 { + void m(num x); +} + +abstract class DD1 { + void m(Object x); +} + +class D1 extends B1 implements DD1 { + void m(num x); +} + +class B2 { + void m(covariant num x) {} +} + +class C2 extends B2 { + void m(int x); +} + +abstract class DD2 { + void m(Object x); +} + +class D2 extends B2 implements DD2 { + void m(int x); +} + +// Superclass: regular, interface: extends. + +class B3 { + void m(covariant num x) {} +} + +abstract class BB3 extends B3 { + void m(num x); +} + +class C3 extends BB3 {} + +abstract class DD3 { + void m(Object x); +} + +class D3 extends BB3 implements DD3 {} + +class B4 { + void m(covariant int x) {} +} + +abstract class BB4 extends B4 { + void m(num x); +} + +class C4 extends BB4 {} + +abstract class DD4 { + void m(Object x); +} + +class D4 extends BB4 implements DD4 {} + +class B5 { + void m(covariant num x) {} +} + +abstract class BB5 extends B5 { + void m(int x); +} + +class C5 extends BB5 {} + +abstract class DD5 { + void m(Object x); +} + +class D5 extends BB5 implements DD5 {} + +// Superclass: regular, interface: implements. + +class B6 { + void m(covariant num x) {} +} + +abstract class I6 { + void m(num x); +} + +class C6 extends B6 implements I6 {} + +abstract class JJ6 { + void m(Object x); +} + +abstract class J6 { + void m(num x); +} + +class D6 extends B6 implements J6, JJ6 {} + +class B7 { + void m(covariant int x) {} +} + +abstract class I7 { + void m(num x); +} + +class C7 extends B7 implements I7 {} + +abstract class JJ7 { + void m(Object x); +} + +abstract class J7 { + void m(num x); +} + +class D7 extends B7 implements J7, JJ7 {} + +class B8 { + void m(covariant num x) {} +} + +abstract class I8 { + void m(int x); +} + +class C8 extends B8 implements I8 {} + +abstract class JJ8 { + void m(Object x); +} + +abstract class J8 { + void m(int x); +} + +class D8 extends B8 implements J8, JJ8 {} + +// Superclass: mixed-in, interface: immediate. + +class A9 {} + +mixin M9 { + void m(covariant num x) {} +} + +class B9 extends A9 with M9 {} + +class C9 extends B9 { + void m(num x); +} + +abstract class DD9 { + void m(Object x); +} + +class D9 extends B9 implements DD9 { + void m(num x); +} + +class A10 {} + +mixin M10 { + void m(covariant int x) {} +} + +class B10 extends A10 with M10 {} + +class C10 extends B10 { + void m(num x); +} + +abstract class DD10 { + void m(Object x); +} + +class D10 extends B10 implements DD10 { + void m(num x); +} + +class A11 {} + +mixin M11 { + void m(covariant num x) {} +} + +class B11 extends A11 with M11 {} + +class C11 extends B11 { + void m(int x); +} + +abstract class DD11 { + void m(Object x); +} + +class D11 extends B11 implements DD11 { + void m(int x); +} + +// Superclass: mixed-in, interface: extends. + +class A12 {} + +mixin M12 { + void m(covariant num x) {} +} + +class B12 extends A12 with M12 {} + +abstract class BB12 extends B12 { + void m(num x); +} + +class C12 extends BB12 {} + +abstract class DD12 { + void m(Object x); +} + +class D12 extends BB12 implements DD12 {} + +class A13 {} + +mixin M13 { + void m(covariant int x) {} +} + +class B13 extends A13 with M13 {} + +abstract class BB13 extends B13 { + void m(num x); +} + +class C13 extends BB13 {} + +abstract class DD13 { + void m(Object x); +} + +class D13 extends BB13 implements DD13 {} + +class A14 {} + +mixin M14 { + void m(covariant num x) {} +} + +class B14 extends A14 with M14 {} + +abstract class BB14 extends B14 { + void m(int x); +} + +class C14 extends BB14 {} + +abstract class DD14 { + void m(Object x); +} + +class D14 extends BB14 implements DD14 {} + +// Superclass: mixed-in, interface: implements. + +class A15 {} + +mixin M15 { + void m(covariant num x) {} +} + +class B15 extends A15 with M15 {} + +abstract class I15 { + void m(num x); +} + +class C15 extends B15 implements I15 {} + +abstract class JJ15 { + void m(Object x); +} + +abstract class J15 { + void m(num x); +} + +class D15 extends B15 implements J15, JJ15 {} + +class A16 {} + +mixin M16 { + void m(covariant int x) {} +} + +class B16 extends A16 with M16 {} + +abstract class I16 { + void m(num x); +} + +class C16 extends B16 implements I16 {} + +abstract class JJ16 { + void m(Object x); +} + +abstract class J16 { + void m(num x); +} + +class D16 extends B16 implements J16, JJ16 {} + +class A17 {} + +mixin M17 { + void m(covariant num x) {} +} + +class B17 extends A17 with M17 {} + +abstract class I17 { + void m(int x); +} + +class C17 extends B17 implements I17 {} + +abstract class JJ17 { + void m(Object x); +} + +abstract class J17 { + void m(int x); +} + +class D17 extends B17 implements J17, JJ17 {} + +void main() { + // Demonstrate that each leaf class requires and has a dynamic + // type check (which is most likely placed in a forwarding stub). + const o = Object(); + + B0 x0 = C0(); + x0.m(0.0); + DD0 y0 = D0(); + Expect.throws(() => y0.m(o)); + + C1 x1 = C1(); + Expect.throws(() => x1.m(1.1)); + DD1 y1 = D1(); + Expect.throws(() => y1.m(o)); + + B2 x2 = C2(); + x2.m(2.2); + DD2 y2 = D2(); + Expect.throws(() => y2.m(o)); + + B3 x3 = C3(); + x3.m(3.3); + DD3 y3 = D3(); + Expect.throws(() => y3.m(o)); + + C4 x4 = C4(); + Expect.throws(() => x4.m(4.4)); + DD4 y4 = D4(); + Expect.throws(() => y4.m(o)); + + B5 x5 = C5(); + x5.m(5.5); + DD5 y5 = D5(); + Expect.throws(() => y5.m(o)); + + B6 x6 = C6(); + x6.m(6.6); + JJ6 y6 = D6(); + Expect.throws(() => y6.m(o)); + + C7 x7 = C7(); + Expect.throws(() => x7.m(7.7)); + JJ7 y7 = D7(); + Expect.throws(() => y7.m(o)); + + B8 x8 = C8(); + x8.m(8.8); + JJ8 y8 = D8(); + Expect.throws(() => y8.m(o)); + + B9 x9 = C9(); + x9.m(1.1); + DD9 y9 = D9(); + Expect.throws(() => y9.m(o)); + + C10 x10 = C10(); + Expect.throws(() => x10.m(10.10)); + DD10 y10 = D10(); + Expect.throws(() => y10.m(o)); + + B11 x11 = C11(); + x11.m(11.11); + DD11 y11 = D11(); + Expect.throws(() => y11.m(o)); + + B12 x12 = C12(); + x12.m(12.12); + DD12 y12 = D12(); + Expect.throws(() => y12.m(o)); + + C13 x13 = C13(); + Expect.throws(() => x13.m(13.13)); + DD13 y13 = D13(); + Expect.throws(() => y13.m(o)); + + B14 x14 = C14(); + x14.m(14.14); + DD14 y14 = D14(); + Expect.throws(() => y14.m(o)); + + B15 x15 = C15(); + x15.m(15.15); + JJ15 y15 = D15(); + Expect.throws(() => y15.m(o)); + + C16 x16 = C16(); + Expect.throws(() => x16.m(16.16)); + JJ16 y16 = D16(); + Expect.throws(() => y16.m(o)); + + B17 x17 = C17(); + x17.m(17.17); + JJ17 y17 = D17(); + Expect.throws(() => y17.m(o)); +}