Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes #2428. Add call member tests for extension types #2438

Merged
merged 3 commits into from
Jan 9, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// Copyright (c) 2023, 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.

/// @assertion Consider an invocation of the extension type member m on the
/// receiver expression e according to the extension type declaration V with
/// actual type arguments T1, ..., Ts. If the invocation includes an actual
/// argument part (possibly including some actual type arguments) then call it
/// args. If the invocation omits args, but includes a list of actual type
/// arguments then call them typeArgs. Assume that V declares the type variables
/// X1, ..., Xs
/// ...
/// Let Dm be the unique declaration named m that V has.
///
/// Evaluation of this invocation proceeds by evaluating e to an object o.
/// ...
/// Otherwise, the following is known: args is included, and Dm is a method. The
/// invocation proceeds to evaluate args to an actual argument list args1. Then
/// it executes the body of Dm in an environment where this and the name of the
/// representation are bound in the same way as in the getter invocation, the
/// type variables of V are bound to the actual values of T1, .. Ts, and the
/// formal parameters of m are bound to args1 in the same way that they would be
/// bound for a normal function call. If the body completes returning an object
/// o2 then the invocation evaluates to o2. If the body throws an object and a
/// stack trace then the invocation completes throwing the same object and stack
/// trace.
///
/// @description Check implicit tear-off of a `call` member
/// @author [email protected]

// SharedOptions=--enable-experiment=inline-class

import "../../Utils/expect.dart";

class C {
String call() => "call from C";
}

extension type ET1(Function it) {
String call() => "call from ET1";
}

extension type ET2(Function it) implements Object {
String call() => "call from ET2";
}

Function get functionGetter1 => ET1(C());

Function get functionGetter2 => ET2(C());

void main() {
Expect.equals("call from ET1", functionGetter1());
Expect.equals("call from ET2", functionGetter2());
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// Copyright (c) 2023, 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.

/// @assertion Consider an invocation of the extension type member m on the
/// receiver expression e according to the extension type declaration V with
/// actual type arguments T1, ..., Ts. If the invocation includes an actual
/// argument part (possibly including some actual type arguments) then call it
/// args. If the invocation omits args, but includes a list of actual type
/// arguments then call them typeArgs. Assume that V declares the type variables
/// X1, ..., Xs
/// ...
/// Let Dm be the unique declaration named m that V has.
///
/// Evaluation of this invocation proceeds by evaluating e to an object o.
/// ...
/// Otherwise, the following is known: args is included, and Dm is a method. The
/// invocation proceeds to evaluate args to an actual argument list args1. Then
/// it executes the body of Dm in an environment where this and the name of the
/// representation are bound in the same way as in the getter invocation, the
/// type variables of V are bound to the actual values of T1, .. Ts, and the
/// formal parameters of m are bound to args1 in the same way that they would be
/// bound for a normal function call. If the body completes returning an object
/// o2 then the invocation evaluates to o2. If the body throws an object and a
/// stack trace then the invocation completes throwing the same object and stack
/// trace.
///
/// @description Check invocation of an extension type `call` member
/// @author [email protected]

// SharedOptions=--enable-experiment=inline-class

import "../../Utils/expect.dart";

class C {
String call() => "call from C";
}

extension type ET1(C _) implements C {}

extension type ET2(int _) {
String call() => "call from ET2";
}

main() {
ET1 e1 = ET1(C());
ET2 e2 = ET2(0);

Expect.equals("call from C", e1());
Expect.equals("call from ET2", e2());
Expect.equals("call from C", e1.call());
Expect.equals("call from ET2", e2.call());
Expect.equals("call from C", (e1.call)());
Expect.equals("call from ET2", (e2.call)());
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// Copyright (c) 2023, 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.

/// @assertion Consider an invocation of the extension type member m on the
/// receiver expression e according to the extension type declaration V with
/// actual type arguments T1, ..., Ts. If the invocation includes an actual
/// argument part (possibly including some actual type arguments) then call it
/// args. If the invocation omits args, but includes a list of actual type
/// arguments then call them typeArgs. Assume that V declares the type variables
/// X1, ..., Xs
/// ...
/// Let Dm be the unique declaration named m that V has.
///
/// Evaluation of this invocation proceeds by evaluating e to an object o.
/// ...
/// Otherwise, the following is known: args is included, and Dm is a method. The
/// invocation proceeds to evaluate args to an actual argument list args1. Then
/// it executes the body of Dm in an environment where this and the name of the
/// representation are bound in the same way as in the getter invocation, the
/// type variables of V are bound to the actual values of T1, .. Ts, and the
/// formal parameters of m are bound to args1 in the same way that they would be
/// bound for a normal function call. If the body completes returning an object
/// o2 then the invocation evaluates to o2. If the body throws an object and a
/// stack trace then the invocation completes throwing the same object and stack
/// trace.
///
/// @description Check that if an extension type has `call()` member then it can
/// be assigned to the type `Function`
/// @author [email protected]

// SharedOptions=--enable-experiment=inline-class

import "../../Utils/expect.dart";

class C {
String call() => "call from C";
}

extension type ET1(C _) implements C {}

extension type ET2(int _) {
String call() => "call from ET2";
}

main() {
ET1 e1 = ET1(C());
ET2 e2 = ET2(0);

Function f1 = e1;
Function f2 = e2;
Function f3 = e1.call;
Function f4 = e2.call;

Expect.equals("call from C", f1());
Expect.equals("call from ET2", f2());
Expect.equals("call from C", f3());
Expect.equals("call from ET2", f4());
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
// Copyright (c) 2023, 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.

/// @assertion Assume that DV is an extension type declaration named Name, and
/// V1 occurs as one of the <type>s in the <interfaces> of DV. In this case we
/// say that V1 is a superinterface of DV.
/// ...
/// Assume that DV is an extension type declaration named Name, and the type V1,
/// declared by DV1, is a superinterface of DV (V1 could be an extension type or
/// a non-extension type). Let m be the name of a member of V1. If DV also
/// declares a member named m then the latter may be considered similar to a
/// declaration that "overrides" the former. However, it should be noted that
/// extension type method invocation is resolved statically, and hence there is
/// no override relationship among the two in the traditional object-oriented
/// sense (that is, it will never occur that the statically known declaration is
/// the member of V1, and the member invoked at run time is the one in DV). A
/// receiver with static type V1 will invoke the declaration in DV1, and a
/// receiver with a static type which is a reference to DV (like Name or
/// Name<...>) will invoke the one in DV.
///
/// Hence, we use a different word to describe the relationship between a member
/// named m of a superinterface, and a member named m which is declared by the
/// subinterface: We say that the latter redeclares the former.
///
/// In particular, if two different declarations of m are inherited from two
/// superinterfaces then the subinterface can resolve the conflict by
/// redeclaring m.
///
/// There is no notion of having a 'correct override relation' here. With
/// extension types, any member signature can redeclare any other member
/// signature with the same name, including the case where a method is
/// redeclared by a getter, or vice versa.
///
/// @description Checks that a `call` member can be redeclared as any other
/// member
/// @author [email protected]

// SharedOptions=--enable-experiment=inline-class

import '../../Utils/expect.dart';

class C1 {
String call() => "call from C1";
}

class C2 {
String get call => "call from C2";
}

class C3 {
void set call(String _) {}
}

extension type ET1(C1 c) {
String call() => "call from ET1";
}

extension type ET2(C2 c) {
String call() => "call from ET2";
}

extension type ET3(C3 c) {
String call() => "call from ET3";
}

extension type ET4(C1 c) implements C1 {
String call() => "call from ET4";
}

extension type ET5(C2 c) implements C2 {
String call() => "call from ET5";
}

extension type ET6(C3 c) implements C3 {
String call() => "call from ET6";
}

main() {
Expect.equals("call from ET1", ET1(C1())());
Expect.equals("call from ET1", ET1(C1()).call());
Expect.equals("call from ET2", ET2(C2())());
Expect.equals("call from ET2", ET2(C2()).call());
Expect.equals("call from ET3", ET3(C3())());
Expect.equals("call from ET3", ET3(C3()).call());
Expect.equals("call from ET4", ET4(C1())());
Expect.equals("call from ET4", ET4(C1()).call());
Expect.equals("call from ET5", ET5(C2())());
Expect.equals("call from ET5", ET5(C2()).call());
Expect.equals("call from ET6", ET6(C3())());
Expect.equals("call from ET6", ET6(C3()).call());
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
// Copyright (c) 2023, 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.

/// @assertion Assume that DV is an extension type declaration named Name, and
/// V1 occurs as one of the <type>s in the <interfaces> of DV. In this case we
/// say that V1 is a superinterface of DV.
/// ...
/// Assume that DV is an extension type declaration named Name, and the type V1,
/// declared by DV1, is a superinterface of DV (V1 could be an extension type or
/// a non-extension type). Let m be the name of a member of V1. If DV also
/// declares a member named m then the latter may be considered similar to a
/// declaration that "overrides" the former. However, it should be noted that
/// extension type method invocation is resolved statically, and hence there is
/// no override relationship among the two in the traditional object-oriented
/// sense (that is, it will never occur that the statically known declaration is
/// the member of V1, and the member invoked at run time is the one in DV). A
/// receiver with static type V1 will invoke the declaration in DV1, and a
/// receiver with a static type which is a reference to DV (like Name or
/// Name<...>) will invoke the one in DV.
///
/// Hence, we use a different word to describe the relationship between a member
/// named m of a superinterface, and a member named m which is declared by the
/// subinterface: We say that the latter redeclares the former.
///
/// In particular, if two different declarations of m are inherited from two
/// superinterfaces then the subinterface can resolve the conflict by
/// redeclaring m.
///
/// There is no notion of having a 'correct override relation' here. With
/// extension types, any member signature can redeclare any other member
/// signature with the same name, including the case where a method is
/// redeclared by a getter, or vice versa.
///
/// @description Checks that a `call` member can be redeclared as any other
/// member
/// @author [email protected]

// SharedOptions=--enable-experiment=inline-class

import '../../Utils/expect.dart';

class C1 {
String call() => "call from C1";
}

class C2 {
String get call => "call from C2";
}

class C3 {
void set call(String _) {}
}

extension type ET1(C1 c) {
String get call => "call from ET1";
}

extension type ET2(C2 c) {
String get call => "call from ET2";
}

extension type ET3(C3 c) {
String get call => "call from ET3";
}

extension type ET4(C1 c) implements C1 {
String get call => "call from ET4";
}

extension type ET5(C2 c) implements C2 {
String get call => "call from ET5";
}

extension type ET6(C3 c) implements C3 {
String get call => "call from ET6";
}

main() {
Expect.equals("call from ET1", ET1(C1()).call);
Expect.equals("call from ET2", ET2(C2()).call);
Expect.equals("call from ET3", ET3(C3()).call);
Expect.equals("call from ET4", ET4(C1()).call);
Expect.equals("call from ET5", ET5(C2()).call);
Expect.equals("call from ET6", ET6(C3()).call);
}
Loading