[cfe] Add verification of tear-off lowering This adds verification that tear-off nodes for which the target requested a lowering, do not occur in the generated AST. Change-Id: Ic5e58d1cc607778d6239ced2bf9509439ae1e92b Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/223080 Reviewed-by: Chloe Stefantsova <cstefantsova@google.com> Commit-Queue: Johnni Winther <johnniwinther@google.com>
diff --git a/pkg/front_end/lib/src/fasta/kernel/verifier.dart b/pkg/front_end/lib/src/fasta/kernel/verifier.dart index 7543cd9..ae560e2 100644 --- a/pkg/front_end/lib/src/fasta/kernel/verifier.dart +++ b/pkg/front_end/lib/src/fasta/kernel/verifier.dart
@@ -29,7 +29,10 @@ import '../type_inference/type_schema.dart' show UnknownType; import 'redirecting_factory_body.dart' - show RedirectingFactoryBody, isRedirectingFactory; + show + RedirectingFactoryBody, + isRedirectingFactory, + isRedirectingFactoryField; List<LocatedMessage> verifyComponent(Component component, Target target, {bool? isOutline, bool? afterConst, bool skipPlatform: false}) { @@ -431,6 +434,89 @@ exitTreeNode(node); } + void _checkConstructorTearOff(Node node, Member tearOffTarget) { + if (tearOffTarget.enclosingLibrary.importUri.scheme == 'dart') { + // Platform libraries are not compilation with test flags and might + // contain tear-offs not expected when testing lowerings. + return; + } + if (currentMember != null && isRedirectingFactoryField(currentMember!)) { + // The encoding of the redirecting factory field uses + // [ConstructorTearOffConstant] nodes also when lowerings are enabled. + return; + } + if (tearOffTarget is Constructor && + target.isConstructorTearOffLoweringEnabled) { + problem( + node is TreeNode ? node : getLastSeenTreeNode(), + '${node.runtimeType} nodes for generative constructors should be ' + 'lowered for target "${target.name}".'); + } + if (tearOffTarget is Procedure && + tearOffTarget.isFactory && + target.isFactoryTearOffLoweringEnabled) { + problem( + node is TreeNode ? node : getLastSeenTreeNode(), + '${node.runtimeType} nodes for factory constructors should be ' + 'lowered for target "${target.name}".'); + } + } + + @override + void visitConstructorTearOff(ConstructorTearOff node) { + _checkConstructorTearOff(node, node.target); + super.visitConstructorTearOff(node); + } + + @override + void visitConstructorTearOffConstant(ConstructorTearOffConstant node) { + _checkConstructorTearOff(node, node.target); + super.visitConstructorTearOffConstant(node); + } + + void _checkTypedefTearOff(Node node) { + if (target.isTypedefTearOffLoweringEnabled) { + problem( + node is TreeNode ? node : getLastSeenTreeNode(), + '${node.runtimeType} nodes for typedefs should be ' + 'lowered for target "${target.name}".'); + } + } + + @override + void visitTypedefTearOff(TypedefTearOff node) { + _checkTypedefTearOff(node); + super.visitTypedefTearOff(node); + } + + @override + void visitTypedefTearOffConstant(TypedefTearOffConstant node) { + _checkTypedefTearOff(node); + super.visitTypedefTearOffConstant(node); + } + + void _checkRedirectingFactoryTearOff(Node node) { + if (target.isRedirectingFactoryTearOffLoweringEnabled) { + problem( + node is TreeNode ? node : getLastSeenTreeNode(), + 'ConstructorTearOff nodes for redirecting factories should be ' + 'lowered for target "${target.name}".'); + } + } + + @override + void visitRedirectingFactoryTearOff(RedirectingFactoryTearOff node) { + _checkRedirectingFactoryTearOff(node); + super.visitRedirectingFactoryTearOff(node); + } + + @override + void visitRedirectingFactoryTearOffConstant( + RedirectingFactoryTearOffConstant node) { + _checkRedirectingFactoryTearOff(node); + super.visitRedirectingFactoryTearOffConstant(node); + } + @override void defaultTreeNode(TreeNode node) { enterTreeNode(node);
diff --git a/pkg/front_end/testcases/dart2js/constructor_tearoff.dart b/pkg/front_end/testcases/dart2js/constructor_tearoff.dart new file mode 100644 index 0000000..b7234a7 --- /dev/null +++ b/pkg/front_end/testcases/dart2js/constructor_tearoff.dart
@@ -0,0 +1,22 @@ +// 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. + +typedef Alias<T extends num> = Class<T>; + +class Class<T> { + Class(); + factory Class.fact() => Class<T>(); + factory Class.redirect() = Class<T>; +} + +const a = Class.new; +const b = Class.fact; +const c = Class.redirect; +const d = Alias.new; +const e = Alias.fact; +const f = Alias.redirect; + +main() { + print('$a$b$c$d$e$f'); +}
diff --git a/pkg/front_end/testcases/dart2js/constructor_tearoff.dart.strong.expect b/pkg/front_end/testcases/dart2js/constructor_tearoff.dart.strong.expect new file mode 100644 index 0000000..46e4364 --- /dev/null +++ b/pkg/front_end/testcases/dart2js/constructor_tearoff.dart.strong.expect
@@ -0,0 +1,46 @@ +library /*isNonNullableByDefault*/; +import self as self; +import "dart:core" as core; + +typedef Alias<T extends core::num> = self::Class<T>; +class Class<T extends core::Object? = dynamic> extends core::Object { + static final field dynamic _redirecting# = <dynamic>[#C1]/*isLegacy*/; + constructor •() → self::Class<self::Class::T%> + : super core::Object::•() + ; + static method _#new#tearOff<T extends core::Object? = dynamic>() → self::Class<self::Class::_#new#tearOff::T%> + return new self::Class::•<self::Class::_#new#tearOff::T%>(); + static factory fact<T extends core::Object? = dynamic>() → self::Class<self::Class::fact::T%> + return new self::Class::•<self::Class::fact::T%>(); + static method _#fact#tearOff<T extends core::Object? = dynamic>() → self::Class<self::Class::_#fact#tearOff::T%> + return self::Class::fact<self::Class::_#fact#tearOff::T%>(); + static factory redirect<T extends core::Object? = dynamic>() → self::Class<self::Class::redirect::T%> + return new self::Class::•<self::Class::redirect::T%>(); + static method _#redirect#tearOff<T extends core::Object? = dynamic>() → self::Class<self::Class::_#redirect#tearOff::T%> + return new self::Class::•<self::Class::_#redirect#tearOff::T%>(); +} +static const field <T extends core::Object? = dynamic>() → self::Class<T%> a = #C2; +static const field <T extends core::Object? = dynamic>() → self::Class<T%> b = #C3; +static const field <T extends core::Object? = dynamic>() → self::Class<T%> c = #C4; +static const field <T extends core::num>() → self::Class<T> d = #C5; +static const field <T extends core::num>() → self::Class<T> e = #C6; +static const field <T extends core::num>() → self::Class<T> f = #C7; +static method main() → dynamic { + core::print("${#C2}${#C3}${#C4}${#C5}${#C6}${#C7}"); +} +static method _#Alias#new#tearOff<T extends core::num>() → self::Class<self::_#Alias#new#tearOff::T> + return new self::Class::•<self::_#Alias#new#tearOff::T>(); +static method _#Alias#fact#tearOff<T extends core::num>() → self::Class<self::_#Alias#fact#tearOff::T> + return self::Class::fact<self::_#Alias#fact#tearOff::T>(); +static method _#Alias#redirect#tearOff<T extends core::num>() → self::Class<self::_#Alias#redirect#tearOff::T> + return self::Class::_#redirect#tearOff<self::_#Alias#redirect#tearOff::T>(); + +constants { + #C1 = constructor-tearoff self::Class::redirect + #C2 = static-tearoff self::Class::_#new#tearOff + #C3 = static-tearoff self::Class::_#fact#tearOff + #C4 = static-tearoff self::Class::_#redirect#tearOff + #C5 = static-tearoff self::_#Alias#new#tearOff + #C6 = static-tearoff self::_#Alias#fact#tearOff + #C7 = static-tearoff self::_#Alias#redirect#tearOff +}
diff --git a/pkg/front_end/testcases/dart2js/constructor_tearoff.dart.strong.transformed.expect b/pkg/front_end/testcases/dart2js/constructor_tearoff.dart.strong.transformed.expect new file mode 100644 index 0000000..46e4364 --- /dev/null +++ b/pkg/front_end/testcases/dart2js/constructor_tearoff.dart.strong.transformed.expect
@@ -0,0 +1,46 @@ +library /*isNonNullableByDefault*/; +import self as self; +import "dart:core" as core; + +typedef Alias<T extends core::num> = self::Class<T>; +class Class<T extends core::Object? = dynamic> extends core::Object { + static final field dynamic _redirecting# = <dynamic>[#C1]/*isLegacy*/; + constructor •() → self::Class<self::Class::T%> + : super core::Object::•() + ; + static method _#new#tearOff<T extends core::Object? = dynamic>() → self::Class<self::Class::_#new#tearOff::T%> + return new self::Class::•<self::Class::_#new#tearOff::T%>(); + static factory fact<T extends core::Object? = dynamic>() → self::Class<self::Class::fact::T%> + return new self::Class::•<self::Class::fact::T%>(); + static method _#fact#tearOff<T extends core::Object? = dynamic>() → self::Class<self::Class::_#fact#tearOff::T%> + return self::Class::fact<self::Class::_#fact#tearOff::T%>(); + static factory redirect<T extends core::Object? = dynamic>() → self::Class<self::Class::redirect::T%> + return new self::Class::•<self::Class::redirect::T%>(); + static method _#redirect#tearOff<T extends core::Object? = dynamic>() → self::Class<self::Class::_#redirect#tearOff::T%> + return new self::Class::•<self::Class::_#redirect#tearOff::T%>(); +} +static const field <T extends core::Object? = dynamic>() → self::Class<T%> a = #C2; +static const field <T extends core::Object? = dynamic>() → self::Class<T%> b = #C3; +static const field <T extends core::Object? = dynamic>() → self::Class<T%> c = #C4; +static const field <T extends core::num>() → self::Class<T> d = #C5; +static const field <T extends core::num>() → self::Class<T> e = #C6; +static const field <T extends core::num>() → self::Class<T> f = #C7; +static method main() → dynamic { + core::print("${#C2}${#C3}${#C4}${#C5}${#C6}${#C7}"); +} +static method _#Alias#new#tearOff<T extends core::num>() → self::Class<self::_#Alias#new#tearOff::T> + return new self::Class::•<self::_#Alias#new#tearOff::T>(); +static method _#Alias#fact#tearOff<T extends core::num>() → self::Class<self::_#Alias#fact#tearOff::T> + return self::Class::fact<self::_#Alias#fact#tearOff::T>(); +static method _#Alias#redirect#tearOff<T extends core::num>() → self::Class<self::_#Alias#redirect#tearOff::T> + return self::Class::_#redirect#tearOff<self::_#Alias#redirect#tearOff::T>(); + +constants { + #C1 = constructor-tearoff self::Class::redirect + #C2 = static-tearoff self::Class::_#new#tearOff + #C3 = static-tearoff self::Class::_#fact#tearOff + #C4 = static-tearoff self::Class::_#redirect#tearOff + #C5 = static-tearoff self::_#Alias#new#tearOff + #C6 = static-tearoff self::_#Alias#fact#tearOff + #C7 = static-tearoff self::_#Alias#redirect#tearOff +}
diff --git a/pkg/front_end/testcases/dart2js/constructor_tearoff.dart.textual_outline.expect b/pkg/front_end/testcases/dart2js/constructor_tearoff.dart.textual_outline.expect new file mode 100644 index 0000000..89d865d --- /dev/null +++ b/pkg/front_end/testcases/dart2js/constructor_tearoff.dart.textual_outline.expect
@@ -0,0 +1,15 @@ +typedef Alias<T extends num> = Class<T>; + +class Class<T> { + Class(); + factory Class.fact() => Class<T>(); + factory Class.redirect() = Class<T>; +} + +const a = Class.new; +const b = Class.fact; +const c = Class.redirect; +const d = Alias.new; +const e = Alias.fact; +const f = Alias.redirect; +main() {}
diff --git a/pkg/front_end/testcases/dart2js/constructor_tearoff.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/dart2js/constructor_tearoff.dart.textual_outline_modelled.expect new file mode 100644 index 0000000..3f714c9 --- /dev/null +++ b/pkg/front_end/testcases/dart2js/constructor_tearoff.dart.textual_outline_modelled.expect
@@ -0,0 +1,14 @@ +class Class<T> { + Class(); + factory Class.fact() => Class<T>(); + factory Class.redirect() = Class<T>; +} + +const a = Class.new; +const b = Class.fact; +const c = Class.redirect; +const d = Alias.new; +const e = Alias.fact; +const f = Alias.redirect; +main() {} +typedef Alias<T extends num> = Class<T>;
diff --git a/pkg/front_end/testcases/dart2js/constructor_tearoff.dart.weak.expect b/pkg/front_end/testcases/dart2js/constructor_tearoff.dart.weak.expect new file mode 100644 index 0000000..46e4364 --- /dev/null +++ b/pkg/front_end/testcases/dart2js/constructor_tearoff.dart.weak.expect
@@ -0,0 +1,46 @@ +library /*isNonNullableByDefault*/; +import self as self; +import "dart:core" as core; + +typedef Alias<T extends core::num> = self::Class<T>; +class Class<T extends core::Object? = dynamic> extends core::Object { + static final field dynamic _redirecting# = <dynamic>[#C1]/*isLegacy*/; + constructor •() → self::Class<self::Class::T%> + : super core::Object::•() + ; + static method _#new#tearOff<T extends core::Object? = dynamic>() → self::Class<self::Class::_#new#tearOff::T%> + return new self::Class::•<self::Class::_#new#tearOff::T%>(); + static factory fact<T extends core::Object? = dynamic>() → self::Class<self::Class::fact::T%> + return new self::Class::•<self::Class::fact::T%>(); + static method _#fact#tearOff<T extends core::Object? = dynamic>() → self::Class<self::Class::_#fact#tearOff::T%> + return self::Class::fact<self::Class::_#fact#tearOff::T%>(); + static factory redirect<T extends core::Object? = dynamic>() → self::Class<self::Class::redirect::T%> + return new self::Class::•<self::Class::redirect::T%>(); + static method _#redirect#tearOff<T extends core::Object? = dynamic>() → self::Class<self::Class::_#redirect#tearOff::T%> + return new self::Class::•<self::Class::_#redirect#tearOff::T%>(); +} +static const field <T extends core::Object? = dynamic>() → self::Class<T%> a = #C2; +static const field <T extends core::Object? = dynamic>() → self::Class<T%> b = #C3; +static const field <T extends core::Object? = dynamic>() → self::Class<T%> c = #C4; +static const field <T extends core::num>() → self::Class<T> d = #C5; +static const field <T extends core::num>() → self::Class<T> e = #C6; +static const field <T extends core::num>() → self::Class<T> f = #C7; +static method main() → dynamic { + core::print("${#C2}${#C3}${#C4}${#C5}${#C6}${#C7}"); +} +static method _#Alias#new#tearOff<T extends core::num>() → self::Class<self::_#Alias#new#tearOff::T> + return new self::Class::•<self::_#Alias#new#tearOff::T>(); +static method _#Alias#fact#tearOff<T extends core::num>() → self::Class<self::_#Alias#fact#tearOff::T> + return self::Class::fact<self::_#Alias#fact#tearOff::T>(); +static method _#Alias#redirect#tearOff<T extends core::num>() → self::Class<self::_#Alias#redirect#tearOff::T> + return self::Class::_#redirect#tearOff<self::_#Alias#redirect#tearOff::T>(); + +constants { + #C1 = constructor-tearoff self::Class::redirect + #C2 = static-tearoff self::Class::_#new#tearOff + #C3 = static-tearoff self::Class::_#fact#tearOff + #C4 = static-tearoff self::Class::_#redirect#tearOff + #C5 = static-tearoff self::_#Alias#new#tearOff + #C6 = static-tearoff self::_#Alias#fact#tearOff + #C7 = static-tearoff self::_#Alias#redirect#tearOff +}
diff --git a/pkg/front_end/testcases/dart2js/constructor_tearoff.dart.weak.outline.expect b/pkg/front_end/testcases/dart2js/constructor_tearoff.dart.weak.outline.expect new file mode 100644 index 0000000..48c9e36 --- /dev/null +++ b/pkg/front_end/testcases/dart2js/constructor_tearoff.dart.weak.outline.expect
@@ -0,0 +1,45 @@ +library /*isNonNullableByDefault*/; +import self as self; +import "dart:core" as core; + +typedef Alias<T extends core::num> = self::Class<T>; +class Class<T extends core::Object? = dynamic> extends core::Object { + static final field dynamic _redirecting# = <dynamic>[self::Class::redirect]/*isLegacy*/; + constructor •() → self::Class<self::Class::T%> + ; + static method _#new#tearOff<T extends core::Object? = dynamic>() → self::Class<self::Class::_#new#tearOff::T%> + return new self::Class::•<self::Class::_#new#tearOff::T%>(); + static factory fact<T extends core::Object? = dynamic>() → self::Class<self::Class::fact::T%> + ; + static method _#fact#tearOff<T extends core::Object? = dynamic>() → self::Class<self::Class::_#fact#tearOff::T%> + return self::Class::fact<self::Class::_#fact#tearOff::T%>(); + static factory redirect<T extends core::Object? = dynamic>() → self::Class<self::Class::redirect::T%> + return new self::Class::•<self::Class::redirect::T%>(); + static method _#redirect#tearOff<T extends core::Object? = dynamic>() → self::Class<self::Class::_#redirect#tearOff::T%> + return new self::Class::•<self::Class::_#redirect#tearOff::T%>(); +} +static const field <T extends core::Object? = dynamic>() → self::Class<T%> a = self::Class::_#new#tearOff; +static const field <T extends core::Object? = dynamic>() → self::Class<T%> b = self::Class::_#fact#tearOff; +static const field <T extends core::Object? = dynamic>() → self::Class<T%> c = self::Class::_#redirect#tearOff; +static const field <T extends core::num>() → self::Class<T> d = self::_#Alias#new#tearOff; +static const field <T extends core::num>() → self::Class<T> e = self::_#Alias#fact#tearOff; +static const field <T extends core::num>() → self::Class<T> f = self::_#Alias#redirect#tearOff; +static method main() → dynamic + ; +static method _#Alias#new#tearOff<T extends core::num>() → self::Class<self::_#Alias#new#tearOff::T> + return new self::Class::•<self::_#Alias#new#tearOff::T>(); +static method _#Alias#fact#tearOff<T extends core::num>() → self::Class<self::_#Alias#fact#tearOff::T> + return self::Class::fact<self::_#Alias#fact#tearOff::T>(); +static method _#Alias#redirect#tearOff<T extends core::num>() → self::Class<self::_#Alias#redirect#tearOff::T> + return self::Class::_#redirect#tearOff<self::_#Alias#redirect#tearOff::T>(); + + +Extra constant evaluation status: +Evaluated: ConstructorTearOff @ org-dartlang-testcase:///constructor_tearoff.dart:7:7 -> ConstructorTearOffConstant(Class.redirect) +Evaluated: StaticTearOff @ org-dartlang-testcase:///constructor_tearoff.dart:13:11 -> StaticTearOffConstant(Class._#new#tearOff) +Evaluated: StaticTearOff @ org-dartlang-testcase:///constructor_tearoff.dart:14:11 -> StaticTearOffConstant(Class._#fact#tearOff) +Evaluated: StaticTearOff @ org-dartlang-testcase:///constructor_tearoff.dart:15:11 -> StaticTearOffConstant(Class._#redirect#tearOff) +Evaluated: StaticTearOff @ org-dartlang-testcase:///constructor_tearoff.dart:16:11 -> StaticTearOffConstant(_#Alias#new#tearOff) +Evaluated: StaticTearOff @ org-dartlang-testcase:///constructor_tearoff.dart:17:11 -> StaticTearOffConstant(_#Alias#fact#tearOff) +Evaluated: StaticTearOff @ org-dartlang-testcase:///constructor_tearoff.dart:18:11 -> StaticTearOffConstant(_#Alias#redirect#tearOff) +Extra constant evaluation: evaluated: 15, effectively constant: 7
diff --git a/pkg/front_end/testcases/dart2js/constructor_tearoff.dart.weak.transformed.expect b/pkg/front_end/testcases/dart2js/constructor_tearoff.dart.weak.transformed.expect new file mode 100644 index 0000000..46e4364 --- /dev/null +++ b/pkg/front_end/testcases/dart2js/constructor_tearoff.dart.weak.transformed.expect
@@ -0,0 +1,46 @@ +library /*isNonNullableByDefault*/; +import self as self; +import "dart:core" as core; + +typedef Alias<T extends core::num> = self::Class<T>; +class Class<T extends core::Object? = dynamic> extends core::Object { + static final field dynamic _redirecting# = <dynamic>[#C1]/*isLegacy*/; + constructor •() → self::Class<self::Class::T%> + : super core::Object::•() + ; + static method _#new#tearOff<T extends core::Object? = dynamic>() → self::Class<self::Class::_#new#tearOff::T%> + return new self::Class::•<self::Class::_#new#tearOff::T%>(); + static factory fact<T extends core::Object? = dynamic>() → self::Class<self::Class::fact::T%> + return new self::Class::•<self::Class::fact::T%>(); + static method _#fact#tearOff<T extends core::Object? = dynamic>() → self::Class<self::Class::_#fact#tearOff::T%> + return self::Class::fact<self::Class::_#fact#tearOff::T%>(); + static factory redirect<T extends core::Object? = dynamic>() → self::Class<self::Class::redirect::T%> + return new self::Class::•<self::Class::redirect::T%>(); + static method _#redirect#tearOff<T extends core::Object? = dynamic>() → self::Class<self::Class::_#redirect#tearOff::T%> + return new self::Class::•<self::Class::_#redirect#tearOff::T%>(); +} +static const field <T extends core::Object? = dynamic>() → self::Class<T%> a = #C2; +static const field <T extends core::Object? = dynamic>() → self::Class<T%> b = #C3; +static const field <T extends core::Object? = dynamic>() → self::Class<T%> c = #C4; +static const field <T extends core::num>() → self::Class<T> d = #C5; +static const field <T extends core::num>() → self::Class<T> e = #C6; +static const field <T extends core::num>() → self::Class<T> f = #C7; +static method main() → dynamic { + core::print("${#C2}${#C3}${#C4}${#C5}${#C6}${#C7}"); +} +static method _#Alias#new#tearOff<T extends core::num>() → self::Class<self::_#Alias#new#tearOff::T> + return new self::Class::•<self::_#Alias#new#tearOff::T>(); +static method _#Alias#fact#tearOff<T extends core::num>() → self::Class<self::_#Alias#fact#tearOff::T> + return self::Class::fact<self::_#Alias#fact#tearOff::T>(); +static method _#Alias#redirect#tearOff<T extends core::num>() → self::Class<self::_#Alias#redirect#tearOff::T> + return self::Class::_#redirect#tearOff<self::_#Alias#redirect#tearOff::T>(); + +constants { + #C1 = constructor-tearoff self::Class::redirect + #C2 = static-tearoff self::Class::_#new#tearOff + #C3 = static-tearoff self::Class::_#fact#tearOff + #C4 = static-tearoff self::Class::_#redirect#tearOff + #C5 = static-tearoff self::_#Alias#new#tearOff + #C6 = static-tearoff self::_#Alias#fact#tearOff + #C7 = static-tearoff self::_#Alias#redirect#tearOff +}