From 5fff7d61e1f4b8b65fa5795f09c268f3a92e1ca3 Mon Sep 17 00:00:00 2001 From: Robert Nystrom Date: Mon, 8 Jun 2020 23:36:46 +0000 Subject: [PATCH] Migrate language_2/propagate to NNBD. Change-Id: I43ab07aebe7918a1265f8dc594b759e85c08e862 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/150264 Auto-Submit: Bob Nystrom Commit-Queue: Erik Ernst Reviewed-by: Erik Ernst --- .../propagate/argument_type_check_test.dart | 27 +++++++++ .../propagate/assert_assignable_test.dart | 47 +++++++++++++++ .../propagate/in_for_update_test.dart | 28 +++++++++ .../propagate/past_constant_test.dart | 21 +++++++ tests/language/propagate/phi_test.dart | 23 ++++++++ tests/language/propagate/propagate2_test.dart | 24 ++++++++ tests/language/propagate/propagate3_test.dart | 57 +++++++++++++++++++ .../propagate/type_propagation_test.dart | 43 ++++++++++++++ 8 files changed, 270 insertions(+) create mode 100644 tests/language/propagate/argument_type_check_test.dart create mode 100644 tests/language/propagate/assert_assignable_test.dart create mode 100644 tests/language/propagate/in_for_update_test.dart create mode 100644 tests/language/propagate/past_constant_test.dart create mode 100644 tests/language/propagate/phi_test.dart create mode 100644 tests/language/propagate/propagate2_test.dart create mode 100644 tests/language/propagate/propagate3_test.dart create mode 100644 tests/language/propagate/type_propagation_test.dart diff --git a/tests/language/propagate/argument_type_check_test.dart b/tests/language/propagate/argument_type_check_test.dart new file mode 100644 index 00000000000..8962cb41ff2 --- /dev/null +++ b/tests/language/propagate/argument_type_check_test.dart @@ -0,0 +1,27 @@ +// Copyright (c) 2012, 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. + +import "package:expect/expect.dart"; + +main() { + Expect.equals("str", foo("str")); +} + +foo(y) { + dynamic x = 3; + for (int i = 0; i < 2; i++) { + // Make sure that we don't think that the type of x is necessarily + // a number and optimize the x + y expression based on that. The + // value of x changes later... + if (i == 1) return bar(x + y); + x = new A(); + } +} + +bar(t) => t; + +class A { + A() {} + operator +(x) => x; +} diff --git a/tests/language/propagate/assert_assignable_test.dart b/tests/language/propagate/assert_assignable_test.dart new file mode 100644 index 00000000000..1e3411960fe --- /dev/null +++ b/tests/language/propagate/assert_assignable_test.dart @@ -0,0 +1,47 @@ +// Copyright (c) 2013, 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. +// Check that type of the AssertAssignable is recomputed correctly. +// VMOptions=--optimization-counter-threshold=10 --no-use-osr + +import "package:expect/expect.dart"; + +class A { + final p; + final _b; + + b() { + try { + return _b; + } catch (e) {} + } + + A(this.p, this._b); +} + +class B extends A { + B(p, b) : super(p, b); +} + +bar(v) { + for (var x = v; x != null; x = x.p) { + if (x.b()) { + return x; + } + } + return null; +} + +foo(v) { + A x = bar(v); + return x != null; +} + +main() { + final a = new A(new B(new A("haha", true), false), false); + + for (var i = 0; i < 20; i++) { + Expect.isTrue(foo(a)); + } + Expect.isTrue(foo(a)); +} diff --git a/tests/language/propagate/in_for_update_test.dart b/tests/language/propagate/in_for_update_test.dart new file mode 100644 index 00000000000..058518e37e4 --- /dev/null +++ b/tests/language/propagate/in_for_update_test.dart @@ -0,0 +1,28 @@ +// Copyright (c) 2012, 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. +// Check that phi type computation in the Dart2Js compiler does the +// correct thing. + +import "package:expect/expect.dart"; + +bar() => 'foo'; + +main() { + Expect.throws(foo1); + Expect.throws(foo2); +} + +foo1() { + var a = bar(); + for (;; a = 1 + a) { + if (a != 'foo') return; + } +} + +foo2() { + var a = bar(); + for (;; a = 1 + a) { + if (a != 'foo') break; + } +} diff --git a/tests/language/propagate/past_constant_test.dart b/tests/language/propagate/past_constant_test.dart new file mode 100644 index 00000000000..095e7060291 --- /dev/null +++ b/tests/language/propagate/past_constant_test.dart @@ -0,0 +1,21 @@ +// Copyright (c) 2015, 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. + +import 'package:expect/expect.dart'; + +foo(x) => x; + +check(y) { + Expect.equals('foo', y); +} + +main() { + var x = foo('foo'); + var y = foo(x); + x = 'constant'; + check( + y); // 'y' should not propagate here unless reference to 'x' is rewritten + foo(x); + foo(x); +} diff --git a/tests/language/propagate/phi_test.dart b/tests/language/propagate/phi_test.dart new file mode 100644 index 00000000000..4c8e3e222ca --- /dev/null +++ b/tests/language/propagate/phi_test.dart @@ -0,0 +1,23 @@ +// Copyright (c) 2012, 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. +// Check that phi type computation in the Dart2Js compiler does the +// correct thing. + +import "package:expect/expect.dart"; + +bar() => 490; +bar2() => 0; + +foo(b) { + var x = bar(); + var x2 = x; + if (b) x2 = bar2(); + var x3 = 9 + x; // Guarantees that x is a number. Dart2js propagated the + // type information back to the phi (for x2). + return x2 + x3; +} + +main() { + Expect.equals(499, foo(true)); +} diff --git a/tests/language/propagate/propagate2_test.dart b/tests/language/propagate/propagate2_test.dart new file mode 100644 index 00000000000..a09041b9317 --- /dev/null +++ b/tests/language/propagate/propagate2_test.dart @@ -0,0 +1,24 @@ +// Copyright (c) 2013, 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. + +// Regression test for dart2js that used to infinite loop on +// speculatively propagating types. + +class Bar { + noSuchMethod(e) => null; +} + +main() { + var d = new Bar(); + + while (false) { + // [input] will change from indexable to unknown: the use line 20 + // changes its decision because [a2] changes its type from unknown to + // null. + var input = ((x) {})(null); + var p2 = input.keys.firstWhere(null); + var a2 = input.keys.firstWhere(null); + print(input[a2] == p2); + } +} diff --git a/tests/language/propagate/propagate3_test.dart b/tests/language/propagate/propagate3_test.dart new file mode 100644 index 00000000000..2848f9b5434 --- /dev/null +++ b/tests/language/propagate/propagate3_test.dart @@ -0,0 +1,57 @@ +// Copyright (c) 2013, 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. + +// Regression test for dart2js that used to generate wrong code for +// it. The bug happened in the SSA type propagation. + +class A { + next() => new B(); + doIt() => null; + bool get isEmpty => false; + foo() => 42; + bar() => 54; +} + +bool entered = false; + +class B extends A { + foo() => 54; + doIt() => new A(); + bool get isEmpty => true; + bar() => entered = true; +} + +// (1) At initialization phase of the type propagation, [a] would be +// marked as [exact A]. +// (2) Will make the loop phi [b] typed [null, exact A]. +// (3) Will create a [HTypeKnown] [exact A] for [b]. +// (4) Will create a [HTypeKnown] [exact A] for [b] and update users +// of [b] to use this [HTypeKnown] instead. +// (5) [a] will be updated to [subclass A]. +// (6) Will change the [HTypeKnown] of [b] from [exact A] to [subclass A]. +// (7) Receiver is [subclass A] and it will refine it to +// [subclass A]. We used to wrongly assume there was +// no need to update the [HTypeKnown] created in (3). +// (8) Consider that bar is called on an [exact A] (the [HTypeKnown] +// created in (3)) and remove the call because it does not have +// any side effects. + +main() { + var a = new A(); + for (var i in [42]) { + a = a.next(); + } + + // (1, 5) + + var b = a; + while (b.isEmpty) { + // (4, 6) + b.foo(); // (3, 7) + b.bar(); // (8) + b = b.doIt(); // (2) + } + + if (!entered) throw 'Test failed'; +} diff --git a/tests/language/propagate/type_propagation_test.dart b/tests/language/propagate/type_propagation_test.dart new file mode 100644 index 00000000000..53501f14ee3 --- /dev/null +++ b/tests/language/propagate/type_propagation_test.dart @@ -0,0 +1,43 @@ +// Copyright (c) 2013, 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. + +// dart2js used to have an infinite loop in its type propagation +// algorithm due to types becoming broader instead of narrower. + +import "package:expect/expect.dart"; + +class A { + resolveSend(node) { + if (node == null) { + return [new B()][0]; + } else { + return [new B(), new A()][1]; + } + } + + visitSend(node) { + var target = resolveSend(node); + + if (false) { + if (false) { + target = target.getter; + if (false) { + target = new Object(); + } + } + } + return true ? target : null; + } +} + +var a = 43; + +class B { + var getter = a == 42 ? new A() : null; +} + +main() { + Expect.isTrue(new A().visitSend(new A()) is A); + Expect.isTrue(new A().visitSend(null) is B); +}