[cfe] Support typedefs in object patterns

Part of https://github.com/dart-lang/sdk/issues/49749

Change-Id: I0d29a875a6a0d3bac5c20309a1b4a6c8b48618f5
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/277960
Reviewed-by: Johnni Winther <johnniwinther@google.com>
Commit-Queue: Chloe Stefantsova <cstefantsova@google.com>
This commit is contained in:
Chloe Stefantsova 2023-01-02 12:26:52 +00:00 committed by Commit Queue
parent 157641dc3d
commit 628dd02bc8
19 changed files with 711 additions and 2 deletions

View file

@ -8347,8 +8347,37 @@ class BodyBuilder extends StackListenerImpl
// TODO(cstefantsova): Handle the case of secondIdentifier != null
handleIdentifier(firstIdentifier, IdentifierContext.typeReference);
Object? resolvedIdentifier = pop();
if (resolvedIdentifier is TypeUseGenerator) {
TypeDeclarationBuilder typeDeclaration = resolvedIdentifier.declaration;
if (typeDeclaration is TypeAliasBuilder) {
if (typeArguments == null ||
typeArguments.length ==
typeDeclaration.typedef.typeParameters.length) {
TypeDeclarationBuilder? unaliasedTypeDeclaration =
typeDeclaration.unaliasDeclaration(typeArguments);
List<TypeBuilder>? unaliasedTypeArguments =
typeDeclaration.unaliasTypeArguments(typeArguments);
if (unaliasedTypeDeclaration == null) {
// TODO(cstefantsova): Make sure an error is reported elsewhere.
push(new DummyPattern(firstIdentifier.charOffset));
return;
} else {
typeDeclaration = unaliasedTypeDeclaration;
typeArguments = unaliasedTypeArguments;
}
} else {
addProblem(
fasta.templateTypeArgumentMismatch
.withArguments(typeDeclaration.typedef.typeParameters.length),
firstIdentifier.charOffset,
noLength);
push(new DummyPattern(firstIdentifier.charOffset));
return;
}
}
if (typeDeclaration is ClassBuilder) {
List<DartType>? builtTypeArguments;
if (typeArguments != null) {
@ -8359,7 +8388,13 @@ class BodyBuilder extends StackListenerImpl
.add(typeBuilder.build(libraryBuilder, TypeUse.typeArgument));
}
} else {
// TODO(cstefantsova): Report an error.
addProblem(
fasta.templateTypeArgumentMismatch
.withArguments(typeDeclaration.cls.typeParameters.length),
firstIdentifier.charOffset,
noLength);
push(new DummyPattern(firstIdentifier.charOffset));
return;
}
}
push(new ObjectPattern(
@ -8368,7 +8403,7 @@ class BodyBuilder extends StackListenerImpl
builtTypeArguments,
firstIdentifier.offset));
} else {
// TODO(cstefantsova): Handle this case.
// TODO(cstefantsova): Handle other possibilities.
push(new DummyPattern(firstIdentifier.charOffset));
}
} else {

View file

@ -0,0 +1,14 @@
// Copyright (c) 2022, 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.
class A {}
typedef B<X> = A;
test(dynamic x) {
if (x case A<int>()) {} // Error.
if (x case B()) {} // Ok: the type argument is inferred.
if (x case B<int>()) {} // Ok.
if (x case B<String, num>()) {} // Error.
}

View file

@ -0,0 +1,35 @@
library /*isNonNullableByDefault*/;
//
// Problems in library:
//
// pkg/front_end/testcases/patterns/object_pattern_errors.dart:10:14: Error: Expected 0 type arguments.
// if (x case A<int>()) {} // Error.
// ^
//
// pkg/front_end/testcases/patterns/object_pattern_errors.dart:13:14: Error: Expected 1 type arguments.
// if (x case B<String, num>()) {} // Error.
// ^
//
import self as self;
import "dart:core" as core;
typedef B<unrelated X extends core::Object? = dynamic> = self::A;
class A extends core::Object {
synthetic constructor •() → self::A
: super core::Object::•()
;
}
static method test(dynamic x) → dynamic {
final dynamic #t1 = x;
final dynamic #t2 = x;
final dynamic #t3 = #t2;
if(#t3 is self::A) {
}
final dynamic #t4 = x;
final dynamic #t5 = #t4;
if(#t5 is self::A) {
}
final dynamic #t6 = x;
}
static method _#B#new#tearOff<unrelated X extends core::Object? = dynamic>() → self::A
return new self::A::•();

View file

@ -0,0 +1,35 @@
library /*isNonNullableByDefault*/;
//
// Problems in library:
//
// pkg/front_end/testcases/patterns/object_pattern_errors.dart:10:14: Error: Expected 0 type arguments.
// if (x case A<int>()) {} // Error.
// ^
//
// pkg/front_end/testcases/patterns/object_pattern_errors.dart:13:14: Error: Expected 1 type arguments.
// if (x case B<String, num>()) {} // Error.
// ^
//
import self as self;
import "dart:core" as core;
typedef B<unrelated X extends core::Object? = dynamic> = self::A;
class A extends core::Object {
synthetic constructor •() → self::A
: super core::Object::•()
;
}
static method test(dynamic x) → dynamic {
final dynamic #t1 = x;
final dynamic #t2 = x;
final dynamic #t3 = #t2;
if(#t3 is self::A) {
}
final dynamic #t4 = x;
final dynamic #t5 = #t4;
if(#t5 is self::A) {
}
final dynamic #t6 = x;
}
static method _#B#new#tearOff<unrelated X extends core::Object? = dynamic>() → self::A
return new self::A::•();

View file

@ -0,0 +1,4 @@
class A {}
typedef B<X> = A;
test(dynamic x) {}

View file

@ -0,0 +1,4 @@
class A {}
test(dynamic x) {}
typedef B<X> = A;

View file

@ -0,0 +1,35 @@
library /*isNonNullableByDefault*/;
//
// Problems in library:
//
// pkg/front_end/testcases/patterns/object_pattern_errors.dart:10:14: Error: Expected 0 type arguments.
// if (x case A<int>()) {} // Error.
// ^
//
// pkg/front_end/testcases/patterns/object_pattern_errors.dart:13:14: Error: Expected 1 type arguments.
// if (x case B<String, num>()) {} // Error.
// ^
//
import self as self;
import "dart:core" as core;
typedef B<unrelated X extends core::Object? = dynamic> = self::A;
class A extends core::Object {
synthetic constructor •() → self::A
: super core::Object::•()
;
}
static method test(dynamic x) → dynamic {
final dynamic #t1 = x;
final dynamic #t2 = x;
final dynamic #t3 = #t2;
if(#t3 is self::A) {
}
final dynamic #t4 = x;
final dynamic #t5 = #t4;
if(#t5 is self::A) {
}
final dynamic #t6 = x;
}
static method _#B#new#tearOff<unrelated X extends core::Object? = dynamic>() → self::A
return new self::A::•();

View file

@ -0,0 +1,35 @@
library /*isNonNullableByDefault*/;
//
// Problems in library:
//
// pkg/front_end/testcases/patterns/object_pattern_errors.dart:10:14: Error: Expected 0 type arguments.
// if (x case A<int>()) {} // Error.
// ^
//
// pkg/front_end/testcases/patterns/object_pattern_errors.dart:13:14: Error: Expected 1 type arguments.
// if (x case B<String, num>()) {} // Error.
// ^
//
import self as self;
import "dart:core" as core;
typedef B<unrelated X extends core::Object? = dynamic> = self::A;
class A extends core::Object {
synthetic constructor •() → self::A
: super core::Object::•()
;
}
static method test(dynamic x) → dynamic {
final dynamic #t1 = x;
final dynamic #t2 = x;
final dynamic #t3 = #t2;
if(#t3 is self::A) {
}
final dynamic #t4 = x;
final dynamic #t5 = #t4;
if(#t5 is self::A) {
}
final dynamic #t6 = x;
}
static method _#B#new#tearOff<unrelated X extends core::Object? = dynamic>() → self::A
return new self::A::•();

View file

@ -0,0 +1,13 @@
library /*isNonNullableByDefault*/;
import self as self;
import "dart:core" as core;
typedef B<unrelated X extends core::Object? = dynamic> = self::A;
class A extends core::Object {
synthetic constructor •() → self::A
;
}
static method test(dynamic x) → dynamic
;
static method _#B#new#tearOff<unrelated X extends core::Object? = dynamic>() → self::A
return new self::A::•();

View file

@ -0,0 +1,35 @@
library /*isNonNullableByDefault*/;
//
// Problems in library:
//
// pkg/front_end/testcases/patterns/object_pattern_errors.dart:10:14: Error: Expected 0 type arguments.
// if (x case A<int>()) {} // Error.
// ^
//
// pkg/front_end/testcases/patterns/object_pattern_errors.dart:13:14: Error: Expected 1 type arguments.
// if (x case B<String, num>()) {} // Error.
// ^
//
import self as self;
import "dart:core" as core;
typedef B<unrelated X extends core::Object? = dynamic> = self::A;
class A extends core::Object {
synthetic constructor •() → self::A
: super core::Object::•()
;
}
static method test(dynamic x) → dynamic {
final dynamic #t1 = x;
final dynamic #t2 = x;
final dynamic #t3 = #t2;
if(#t3 is self::A) {
}
final dynamic #t4 = x;
final dynamic #t5 = #t4;
if(#t5 is self::A) {
}
final dynamic #t6 = x;
}
static method _#B#new#tearOff<unrelated X extends core::Object? = dynamic>() → self::A
return new self::A::•();

View file

@ -0,0 +1,52 @@
// Copyright (c) 2022, 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.
class A {
int foo;
A(this.foo);
}
typedef B = A;
class C<X, Y> {
X x;
Y y;
C(this.x, this.y);
}
typedef D<X> = C<X, X>;
test1(dynamic x) {
if (x case B(:var foo)) {
return foo;
} else {
return null;
}
}
test2(dynamic x) {
if (x case D<String>(:var x)) {
return x;
} else {
return null;
}
}
main() {
expectEquals(0, test1(new A(0)));
expectEquals(1, test1(new B(1)));
expectEquals(null, test1(null));
expectEquals("one", test2(new C("one", "two")));
expectEquals("one", test2(new D("one", "two")));
expectEquals(null, test2(null));
}
expectEquals(x, y) {
if (x != y) {
throw "Expected ${x} to be equal to ${y}.";
}
}

View file

@ -0,0 +1,70 @@
library /*isNonNullableByDefault*/;
import self as self;
import "dart:core" as core;
typedef B = self::A;
typedef D<X extends core::Object? = dynamic> = self::C<X%, X%>;
class A extends core::Object {
field core::int foo;
constructor •(core::int foo) → self::A
: self::A::foo = foo, super core::Object::•()
;
}
class C<X extends core::Object? = dynamic, Y extends core::Object? = dynamic> extends core::Object {
covariant-by-class field self::C::X% x;
covariant-by-class field self::C::Y% y;
constructor •(self::C::X% x, self::C::Y% y) → self::C<self::C::X%, self::C::Y%>
: self::C::x = x, self::C::y = y, super core::Object::•()
;
}
static method test1(dynamic x) → dynamic {
final dynamic #t1 = x;
final core::bool #t2 = true;
final dynamic #t3 = #t1;
if(#t3 is self::A) {
final core::int #t4 = #t3{self::A}.{self::A::foo}{core::int};
#t2 = false;
{
dynamic foo = #t4;
{
return foo;
}
}
}
if(#t2) {
return null;
}
}
static method test2(dynamic x) → dynamic {
final dynamic #t5 = x;
final core::bool #t6 = true;
final dynamic #t7 = #t5;
if(#t7 is self::C<core::String, core::String>) {
final core::String #t8 = #t7{self::C<core::String, core::String>}.{self::C::x}{core::String};
#t6 = false;
{
dynamic x = #t8;
{
return x;
}
}
}
if(#t6) {
return null;
}
}
static method main() → dynamic {
self::expectEquals(0, self::test1(new self::A::•(0)));
self::expectEquals(1, self::test1(new self::A::•(1)));
self::expectEquals(null, self::test1(null));
self::expectEquals("one", self::test2(new self::C::•<core::String, core::String>("one", "two")));
self::expectEquals("one", self::test2(new self::C::•<core::String, core::String>("one", "two")));
self::expectEquals(null, self::test2(null));
}
static method expectEquals(dynamic x, dynamic y) → dynamic {
if(!(x =={core::Object::==}{(core::Object) → core::bool} y)) {
throw "Expected ${x} to be equal to ${y}.";
}
}
static method _#D#new#tearOff<X extends core::Object? = dynamic>(self::_#D#new#tearOff::X% x, self::_#D#new#tearOff::X% y) → self::C<self::_#D#new#tearOff::X%, self::_#D#new#tearOff::X%>
return new self::C::•<self::_#D#new#tearOff::X%, self::_#D#new#tearOff::X%>(x, y);

View file

@ -0,0 +1,70 @@
library /*isNonNullableByDefault*/;
import self as self;
import "dart:core" as core;
typedef B = self::A;
typedef D<X extends core::Object? = dynamic> = self::C<X%, X%>;
class A extends core::Object {
field core::int foo;
constructor •(core::int foo) → self::A
: self::A::foo = foo, super core::Object::•()
;
}
class C<X extends core::Object? = dynamic, Y extends core::Object? = dynamic> extends core::Object {
covariant-by-class field self::C::X% x;
covariant-by-class field self::C::Y% y;
constructor •(self::C::X% x, self::C::Y% y) → self::C<self::C::X%, self::C::Y%>
: self::C::x = x, self::C::y = y, super core::Object::•()
;
}
static method test1(dynamic x) → dynamic {
final dynamic #t1 = x;
final core::bool #t2 = true;
final dynamic #t3 = #t1;
if(#t3 is self::A) {
final core::int #t4 = #t3{self::A}.{self::A::foo}{core::int};
#t2 = false;
{
dynamic foo = #t4;
{
return foo;
}
}
}
if(#t2) {
return null;
}
}
static method test2(dynamic x) → dynamic {
final dynamic #t5 = x;
final core::bool #t6 = true;
final dynamic #t7 = #t5;
if(#t7 is self::C<core::String, core::String>) {
final core::String #t8 = #t7{self::C<core::String, core::String>}.{self::C::x}{core::String};
#t6 = false;
{
dynamic x = #t8;
{
return x;
}
}
}
if(#t6) {
return null;
}
}
static method main() → dynamic {
self::expectEquals(0, self::test1(new self::A::•(0)));
self::expectEquals(1, self::test1(new self::A::•(1)));
self::expectEquals(null, self::test1(null));
self::expectEquals("one", self::test2(new self::C::•<core::String, core::String>("one", "two")));
self::expectEquals("one", self::test2(new self::C::•<core::String, core::String>("one", "two")));
self::expectEquals(null, self::test2(null));
}
static method expectEquals(dynamic x, dynamic y) → dynamic {
if(!(x =={core::Object::==}{(core::Object) → core::bool} y)) {
throw "Expected ${x} to be equal to ${y}.";
}
}
static method _#D#new#tearOff<X extends core::Object? = dynamic>(self::_#D#new#tearOff::X% x, self::_#D#new#tearOff::X% y) → self::C<self::_#D#new#tearOff::X%, self::_#D#new#tearOff::X%>
return new self::C::•<self::_#D#new#tearOff::X%, self::_#D#new#tearOff::X%>(x, y);

View file

@ -0,0 +1,18 @@
class A {
int foo;
A(this.foo);
}
typedef B = A;
class C<X, Y> {
X x;
Y y;
C(this.x, this.y);
}
typedef D<X> = C<X, X>;
test1(dynamic x) {}
test2(dynamic x) {}
main() {}
expectEquals(x, y) {}

View file

@ -0,0 +1,17 @@
class A {
A(this.foo);
int foo;
}
class C<X, Y> {
C(this.x, this.y);
X x;
Y y;
}
expectEquals(x, y) {}
main() {}
test1(dynamic x) {}
test2(dynamic x) {}
typedef B = A;
typedef D<X> = C<X, X>;

View file

@ -0,0 +1,70 @@
library /*isNonNullableByDefault*/;
import self as self;
import "dart:core" as core;
typedef B = self::A;
typedef D<X extends core::Object? = dynamic> = self::C<X%, X%>;
class A extends core::Object {
field core::int foo;
constructor •(core::int foo) → self::A
: self::A::foo = foo, super core::Object::•()
;
}
class C<X extends core::Object? = dynamic, Y extends core::Object? = dynamic> extends core::Object {
covariant-by-class field self::C::X% x;
covariant-by-class field self::C::Y% y;
constructor •(self::C::X% x, self::C::Y% y) → self::C<self::C::X%, self::C::Y%>
: self::C::x = x, self::C::y = y, super core::Object::•()
;
}
static method test1(dynamic x) → dynamic {
final dynamic #t1 = x;
final core::bool #t2 = true;
final dynamic #t3 = #t1;
if(#t3 is self::A) {
final core::int #t4 = #t3{self::A}.{self::A::foo}{core::int};
#t2 = false;
{
dynamic foo = #t4;
{
return foo;
}
}
}
if(#t2) {
return null;
}
}
static method test2(dynamic x) → dynamic {
final dynamic #t5 = x;
final core::bool #t6 = true;
final dynamic #t7 = #t5;
if(#t7 is self::C<core::String, core::String>) {
final core::String #t8 = #t7{self::C<core::String, core::String>}.{self::C::x}{core::String};
#t6 = false;
{
dynamic x = #t8;
{
return x;
}
}
}
if(#t6) {
return null;
}
}
static method main() → dynamic {
self::expectEquals(0, self::test1(new self::A::•(0)));
self::expectEquals(1, self::test1(new self::A::•(1)));
self::expectEquals(null, self::test1(null));
self::expectEquals("one", self::test2(new self::C::•<core::String, core::String>("one", "two")));
self::expectEquals("one", self::test2(new self::C::•<core::String, core::String>("one", "two")));
self::expectEquals(null, self::test2(null));
}
static method expectEquals(dynamic x, dynamic y) → dynamic {
if(!(x =={core::Object::==}{(core::Object) → core::bool} y)) {
throw "Expected ${x} to be equal to ${y}.";
}
}
static method _#D#new#tearOff<X extends core::Object? = dynamic>(self::_#D#new#tearOff::X% x, self::_#D#new#tearOff::X% y) → self::C<self::_#D#new#tearOff::X%, self::_#D#new#tearOff::X%>
return new self::C::•<self::_#D#new#tearOff::X%, self::_#D#new#tearOff::X%>(x, y);

View file

@ -0,0 +1,70 @@
library /*isNonNullableByDefault*/;
import self as self;
import "dart:core" as core;
typedef B = self::A;
typedef D<X extends core::Object? = dynamic> = self::C<X%, X%>;
class A extends core::Object {
field core::int foo;
constructor •(core::int foo) → self::A
: self::A::foo = foo, super core::Object::•()
;
}
class C<X extends core::Object? = dynamic, Y extends core::Object? = dynamic> extends core::Object {
covariant-by-class field self::C::X% x;
covariant-by-class field self::C::Y% y;
constructor •(self::C::X% x, self::C::Y% y) → self::C<self::C::X%, self::C::Y%>
: self::C::x = x, self::C::y = y, super core::Object::•()
;
}
static method test1(dynamic x) → dynamic {
final dynamic #t1 = x;
final core::bool #t2 = true;
final dynamic #t3 = #t1;
if(#t3 is self::A) {
final core::int #t4 = #t3{self::A}.{self::A::foo}{core::int};
#t2 = false;
{
dynamic foo = #t4;
{
return foo;
}
}
}
if(#t2) {
return null;
}
}
static method test2(dynamic x) → dynamic {
final dynamic #t5 = x;
final core::bool #t6 = true;
final dynamic #t7 = #t5;
if(#t7 is self::C<core::String, core::String>) {
final core::String #t8 = #t7{self::C<core::String, core::String>}.{self::C::x}{core::String};
#t6 = false;
{
dynamic x = #t8;
{
return x;
}
}
}
if(#t6) {
return null;
}
}
static method main() → dynamic {
self::expectEquals(0, self::test1(new self::A::•(0)));
self::expectEquals(1, self::test1(new self::A::•(1)));
self::expectEquals(null, self::test1(null));
self::expectEquals("one", self::test2(new self::C::•<core::String, core::String>("one", "two")));
self::expectEquals("one", self::test2(new self::C::•<core::String, core::String>("one", "two")));
self::expectEquals(null, self::test2(null));
}
static method expectEquals(dynamic x, dynamic y) → dynamic {
if(!(x =={core::Object::==}{(core::Object) → core::bool} y)) {
throw "Expected ${x} to be equal to ${y}.";
}
}
static method _#D#new#tearOff<X extends core::Object? = dynamic>(self::_#D#new#tearOff::X% x, self::_#D#new#tearOff::X% y) → self::C<self::_#D#new#tearOff::X%, self::_#D#new#tearOff::X%>
return new self::C::•<self::_#D#new#tearOff::X%, self::_#D#new#tearOff::X%>(x, y);

View file

@ -0,0 +1,27 @@
library /*isNonNullableByDefault*/;
import self as self;
import "dart:core" as core;
typedef B = self::A;
typedef D<X extends core::Object? = dynamic> = self::C<X%, X%>;
class A extends core::Object {
field core::int foo;
constructor •(core::int foo) → self::A
;
}
class C<X extends core::Object? = dynamic, Y extends core::Object? = dynamic> extends core::Object {
covariant-by-class field self::C::X% x;
covariant-by-class field self::C::Y% y;
constructor •(self::C::X% x, self::C::Y% y) → self::C<self::C::X%, self::C::Y%>
;
}
static method test1(dynamic x) → dynamic
;
static method test2(dynamic x) → dynamic
;
static method main() → dynamic
;
static method expectEquals(dynamic x, dynamic y) → dynamic
;
static method _#D#new#tearOff<X extends core::Object? = dynamic>(self::_#D#new#tearOff::X% x, self::_#D#new#tearOff::X% y) → self::C<self::_#D#new#tearOff::X%, self::_#D#new#tearOff::X%>
return new self::C::•<self::_#D#new#tearOff::X%, self::_#D#new#tearOff::X%>(x, y);

View file

@ -0,0 +1,70 @@
library /*isNonNullableByDefault*/;
import self as self;
import "dart:core" as core;
typedef B = self::A;
typedef D<X extends core::Object? = dynamic> = self::C<X%, X%>;
class A extends core::Object {
field core::int foo;
constructor •(core::int foo) → self::A
: self::A::foo = foo, super core::Object::•()
;
}
class C<X extends core::Object? = dynamic, Y extends core::Object? = dynamic> extends core::Object {
covariant-by-class field self::C::X% x;
covariant-by-class field self::C::Y% y;
constructor •(self::C::X% x, self::C::Y% y) → self::C<self::C::X%, self::C::Y%>
: self::C::x = x, self::C::y = y, super core::Object::•()
;
}
static method test1(dynamic x) → dynamic {
final dynamic #t1 = x;
final core::bool #t2 = true;
final dynamic #t3 = #t1;
if(#t3 is self::A) {
final core::int #t4 = #t3{self::A}.{self::A::foo}{core::int};
#t2 = false;
{
dynamic foo = #t4;
{
return foo;
}
}
}
if(#t2) {
return null;
}
}
static method test2(dynamic x) → dynamic {
final dynamic #t5 = x;
final core::bool #t6 = true;
final dynamic #t7 = #t5;
if(#t7 is self::C<core::String, core::String>) {
final core::String #t8 = #t7{self::C<core::String, core::String>}.{self::C::x}{core::String};
#t6 = false;
{
dynamic x = #t8;
{
return x;
}
}
}
if(#t6) {
return null;
}
}
static method main() → dynamic {
self::expectEquals(0, self::test1(new self::A::•(0)));
self::expectEquals(1, self::test1(new self::A::•(1)));
self::expectEquals(null, self::test1(null));
self::expectEquals("one", self::test2(new self::C::•<core::String, core::String>("one", "two")));
self::expectEquals("one", self::test2(new self::C::•<core::String, core::String>("one", "two")));
self::expectEquals(null, self::test2(null));
}
static method expectEquals(dynamic x, dynamic y) → dynamic {
if(!(x =={core::Object::==}{(core::Object) → core::bool} y)) {
throw "Expected ${x} to be equal to ${y}.";
}
}
static method _#D#new#tearOff<X extends core::Object? = dynamic>(self::_#D#new#tearOff::X% x, self::_#D#new#tearOff::X% y) → self::C<self::_#D#new#tearOff::X%, self::_#D#new#tearOff::X%>
return new self::C::•<self::_#D#new#tearOff::X%, self::_#D#new#tearOff::X%>(x, y);