Add language tests for issues 33298 and 56666.
Yesterday I discovered some code in the front end type inference engine that I didn't expect: logic that added constraints to type inference based on the result of applying type coercions. To figure out why it was necessary, I disabled it and ran the code through trybots. It turns out that it's needed to prevent the following issues: - https://github.com/dart-lang/sdk/issues/33298 - https://github.com/dart-lang/sdk/issues/56666 Fortunately, we had regression tests for these issues: - `pkg/front_end/testcases/general/bug33298.dart` - `pkg/front_end/testcases/general/issue56666.dart` Unfortunately, those regression tests are front-end specific, meaning we didn't have any coverage for the analyzer. This CL adds coverage for the analyzer by replicating the regression tests in `tests/language`. Change-Id: Ia53b3c898549e991d6685a414d177a466a6a6964 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/494563 Commit-Queue: Paul Berry <paulberry@google.com> Reviewed-by: Lasse Nielsen <lrn@google.com>
This commit is contained in:
committed by
dart-scoped@luci-project-accounts.iam.gserviceaccount.com
parent
00b6621e50
commit
25bb8ce922
@@ -0,0 +1,58 @@
|
||||
// Copyright (c) 2026, 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.
|
||||
|
||||
// This test is based on the repro for
|
||||
// https://github.com/dart-lang/sdk/issues/33298. It illustrates that the types
|
||||
// that arise from a type coercion need to be accounted for in type inference.
|
||||
//
|
||||
// Specifically, these tests verify that, after type inference has finished
|
||||
// visiting all the arguments of an invocation, made a preliminary assignment of
|
||||
// types to type parameters, and then performed assignability checks on each of
|
||||
// the arguments, if any of those assignability checks resulted in the insertion
|
||||
// of a coercion, then the static type of the coerced expression is then used to
|
||||
// generate additional type constraints.
|
||||
//
|
||||
// For example, in the invocation `List<String> list2 = ['a', 'b',
|
||||
// 'c'].map(a).toList()` below, the assignability check to see if `a` is usable
|
||||
// as an argument to `map` results in a coercion, causing `a` to be treated as
|
||||
// `a.call`. After this coercion is generated, type inference needs to then use
|
||||
// the static type of `a.call` to generate additional type constraints. This
|
||||
// results in a constraint that the type argument to `map` must be a supertype
|
||||
// of `String`, which in turn ensures that the type of `['a', 'b', 'c'].map(a)`
|
||||
// is `Iterable<String>`. Without this extra constraint generation step, the
|
||||
// type of `['a', 'b', 'c']` would be `Iterable<dynamic>`.
|
||||
|
||||
import 'package:expect/expect.dart';
|
||||
|
||||
class A {
|
||||
String call(String s) => '$s$s';
|
||||
}
|
||||
|
||||
class B<T> {
|
||||
T call(T t) => t;
|
||||
}
|
||||
|
||||
class C {
|
||||
T call<T>(T t) => t;
|
||||
}
|
||||
|
||||
main() {
|
||||
A a = A();
|
||||
List<String> list1 = ['a', 'b', 'c'].map(a.call).toList();
|
||||
Expect.listEquals(['aa', 'bb', 'cc'], list1);
|
||||
List<String> list2 = ['a', 'b', 'c'].map(a).toList();
|
||||
Expect.listEquals(['aa', 'bb', 'cc'], list2);
|
||||
|
||||
B<String> b = B();
|
||||
List<String> list3 = ['a', 'b', 'c'].map(b.call).toList();
|
||||
Expect.listEquals(['a', 'b', 'c'], list3);
|
||||
List<String> list4 = ['a', 'b', 'c'].map(b).toList();
|
||||
Expect.listEquals(['a', 'b', 'c'], list4);
|
||||
|
||||
C c = C();
|
||||
List<String> list5 = ['a', 'b', 'c'].map(c.call).toList();
|
||||
Expect.listEquals(['a', 'b', 'c'], list5);
|
||||
List<String> list6 = ['a', 'b', 'c'].map(c).toList();
|
||||
Expect.listEquals(['a', 'b', 'c'], list6);
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
// Copyright (c) 2026, 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.
|
||||
|
||||
// This test is based on the repro for
|
||||
// https://github.com/dart-lang/sdk/issues/56666. It illustrates that the types
|
||||
// that arise from a type coercion need to be accounted for in type inference.
|
||||
//
|
||||
// Specifically, these tests verify that, after type inference has finished
|
||||
// visiting all the arguments of an invocation, made a preliminary assignment of
|
||||
// types to type parameters, and then performed assignability checks on each of
|
||||
// the arguments, if any of those assignability checks resulted in the insertion
|
||||
// of a coercion, then the static type of the coerced expression is then used to
|
||||
// generate additional type constraints.
|
||||
//
|
||||
// For example, in the invocation `var g = f(C());` below, the assignability
|
||||
// check to see if `C()` is usable as an argument to `f` results in a coercion,
|
||||
// causing `C()` to be treated as `C().call`. After this coercion is generated,
|
||||
// type inference needs to then use the static type of `C().call` to generate
|
||||
// additional type constraints. This results in a constraint that the type
|
||||
// argument to `f` must be a supertype of `String`, which in turn ensures that
|
||||
// the type of `f(C())` is `String Function(String)`. Without this extra
|
||||
// constraint generation step, the type of `f(C())` would be `dynamic
|
||||
// Function(String)`.
|
||||
|
||||
import 'package:expect/expect.dart';
|
||||
import '../static_type_helper.dart';
|
||||
|
||||
class C {
|
||||
T call<T>(T t) => t;
|
||||
}
|
||||
|
||||
X Function(String) f<X>(X Function(String) g) => g;
|
||||
|
||||
void main() {
|
||||
var g = f(C());
|
||||
g.expectStaticType<Exactly<String Function(String)>>();
|
||||
Expect.equals('s', g('s'));
|
||||
}
|
||||
Reference in New Issue
Block a user