[Language] Various NNBD Tests.

Add a variety of small nnbd tests covering the specification
changes/clarifications landed in
https://github.com/dart-lang/language/pull/1003 .

Change-Id: I0716f14652128323bf103df154efb5bf978091d0
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/150160
Commit-Queue: Leaf Petersen <leafp@google.com>
Reviewed-by: Bob Nystrom <rnystrom@google.com>
This commit is contained in:
Leaf Petersen
2020-06-05 23:53:20 +00:00
committed by commit-bot@chromium.org
parent c48263e901
commit db36f1190f
8 changed files with 558 additions and 0 deletions
@@ -28,4 +28,39 @@ main() {
Expect.isTrue(h is int Function(String));
Expect.isFalse(h is int Function(Object));
Expect.isFalse(h is int Function(Object?));
int Function(Null) i = (Null x) => 1; // Runtime type is int Function(Null)
Expect.isTrue(i is int Function(Null));
Expect.isTrue(i is int Function(Never));
Expect.isFalse(i is int Function(String));
Expect.isFalse(i is int Function(Object));
Expect.isFalse(i is int Function(Object?));
int Function(Never) j = (Never x) => 1; // Runtime type is int Function(Never)
Expect.isFalse(j is int Function(Null));
Expect.isTrue(j is int Function(Never));
Expect.isFalse(j is int Function(String));
Expect.isFalse(j is int Function(Object));
Expect.isFalse(j is int Function(Object?));
// Test that the criteria used for weakening the parameter type
// is that the downwards context parameter type is a subtype of Null
void test<X extends Null, Y extends Never>() {
int Function(X) f = (x) => 1; // Runtime type is int Function(Object?)
Expect.isTrue(f is int Function(Null));
Expect.isTrue(f is int Function(Never));
Expect.isTrue(f is int Function(String));
Expect.isTrue(f is int Function(Object));
Expect.isTrue(f is int Function(Object?));
int Function(Y) g = (x) => 1; // Runtime type is int Function(Object?)
Expect.isTrue(g is int Function(Null));
Expect.isTrue(g is int Function(Never));
Expect.isTrue(g is int Function(String));
Expect.isTrue(g is int Function(Object));
Expect.isTrue(g is int Function(Object?));
}
test<Null, Never>();
test<Never, Never>();
}
@@ -0,0 +1,45 @@
// Copyright (c) 2020, 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.
// Requirements=nnbd-strong
/// Test instance checks and casts in constants may use potentially constant
/// types, and cause compile time errors when the casts fail.
class C1<T> {
final t;
/// Check casts to T
const C1.test(dynamic x) : t = x as T;
}
class C2<T> {
final l;
/// Check casts to List<T>
const C2.test(dynamic x) : l = x as List<T>;
}
void main() {
const c1 = C1<int>.test("hello");
// ^
// [analyzer] unspecified
// [cfe] Constant evaluation error:
const c2 = C1<int>.test(null);
// ^
// [analyzer] unspecified
// [cfe] unspecified
const c3 = C2<int>.test(<num>[0]);
// ^
// [analyzer] unspecified
// [cfe] Constant evaluation error:
const c4 = C2<int>.test("hello");
// ^
// [analyzer] unspecified
// [cfe] Constant evaluation error:
const c5 = C2<int>.test(null);
// ^
// [analyzer] unspecified
// [cfe] unspecified
}
@@ -0,0 +1,143 @@
// Copyright (c) 2020, 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.
// Requirements=nnbd-strong
/// Test instance checks and casts in constants may use potentially constant
/// types, and evaluate appropriately.
import 'package:expect/expect.dart';
class C<T> {
final bool isT;
final bool isListT;
final T? t;
final List<T>? l;
/// Check instance tests in isolation
const C.test1(dynamic x)
: isT = x is T,
isListT = x is List<T>,
t = null,
l = null;
/// Check casts to T in isolation
const C.test2(dynamic x)
: isT = true,
isListT = false,
t = x as T,
l = null;
/// Check casts to List<T> in isolation
const C.test3(dynamic x)
: isT = false,
isListT = true,
t = null,
l = x as List<T>;
/// Combine instance checks with casts, conditional expressions, promotion
const C.test4(dynamic x)
: isT = x is T,
isListT = x is List<T>,
t = (x is T) ? x : null,
l = (x is List<T>) ? x : null;
}
void main() {
{
// Test instance checks of T
const c1 = C<int>.test1(0);
const c2 = C<int>.test1(0);
const c3 = C<int>.test1(1);
const c4 = C<int>.test1("hello");
const c5 = C<int>.test1(null);
Expect.identical(c1, c2);
Expect.identical(c1, c3);
Expect.notIdentical(c1, c4);
Expect.notIdentical(c1, c5);
Expect.isTrue(c1.isT);
Expect.isTrue(c2.isT);
Expect.isTrue(c3.isT);
Expect.isFalse(c4.isT);
Expect.isFalse(c5.isT);
Expect.isFalse(c1.isListT);
Expect.isFalse(c2.isListT);
Expect.isFalse(c3.isListT);
Expect.isFalse(c4.isListT);
Expect.isFalse(c5.isListT);
}
{
// Test instance checks of List<T>
const c1 = C<int>.test1(<int>[0]);
const c2 = C<int>.test1(<int>[0]);
const c3 = C<int>.test1(<int>[1]);
const c4 = C<int>.test1(<num>[1]);
const c5 = C<num>.test1(<int>[1]);
Expect.identical(c1, c2);
Expect.identical(c1, c3);
Expect.notIdentical(c1, c4);
Expect.notIdentical(c1, c5);
Expect.notIdentical(c4, c5);
Expect.isFalse(c1.isT);
Expect.isFalse(c2.isT);
Expect.isFalse(c3.isT);
Expect.isFalse(c4.isT);
Expect.isFalse(c5.isT);
Expect.isTrue(c1.isListT);
Expect.isTrue(c2.isListT);
Expect.isTrue(c3.isListT);
Expect.isFalse(c4.isListT);
Expect.isTrue(c5.isListT);
}
{
// Test casts to T
const c1 = C<int>.test2(0);
const c2 = C<int>.test2(0);
const c3 = C<num>.test2(1);
Expect.identical(c1, c2);
Expect.notIdentical(c1, c3);
}
{
// Test casts to List<T>
const c1 = C<int>.test3(<int>[0]);
const c2 = C<int>.test3(<int>[0]);
const c3 = C<num>.test3(<int>[0]);
Expect.identical(c1, c2);
Expect.notIdentical(c1, c3);
}
{
// Combined tests
const c1 = C<num>.test4(0);
const c2 = C<num>.test4("hello");
const c3 = C<num>.test4(<int>[0]);
const c4 = C<num>.test4(<String>["hello"]);
const c5 = C<int>.test4(<num>[0]);
Expect.isTrue(c1.isT);
Expect.isFalse(c1.isListT);
Expect.equals(c1.t, 0);
Expect.equals(c1.l, null);
Expect.isFalse(c2.isT);
Expect.isFalse(c2.isListT);
Expect.equals(c2.t, null);
Expect.equals(c2.l, null);
Expect.isFalse(c3.isT);
Expect.isTrue(c3.isListT);
Expect.equals(c3.t, null);
Expect.identical(c3.l, const <int>[0]);
Expect.isFalse(c4.isT);
Expect.isFalse(c4.isListT);
Expect.equals(c4.t, null);
Expect.equals(c4.l, null);
Expect.isFalse(c5.isT);
Expect.isFalse(c5.isListT);
Expect.equals(c5.t, null);
Expect.equals(c5.l, null);
}
}
@@ -0,0 +1,126 @@
// Copyright (c) 2020, 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.
// Requirements=nnbd-strong
/// Test that `Never` has all members, and that as a result no extensions
/// apply to it. Also test that `Never` is inferred as the type of a
/// throw expression, and as the return type of function literals that
/// never return.
import 'package:expect/expect.dart';
/// A call of the form staticErrorIfNotNever(t) will produce a static error
/// unless `t` has type `Never`.
void staticErrorIfNotNever<T extends Never>(T t) {}
void neverHasAllMembers(Never x) {
{
// getters
var t = x.arglebargle;
staticErrorIfNotNever(t);
}
{
// setter
x.arglebargle = 3;
}
{
// operator[]
x[0] = 3;
}
{
// methods
var t = x.arglebargle(0);
staticErrorIfNotNever(t);
}
{
// methods with named parameters
var t = x.arglebargle(foo: 0);
staticErrorIfNotNever(t);
}
{
// call method
var t = x(3);
staticErrorIfNotNever(t);
}
{
// Object members
staticErrorIfNotNever(x.toString());
staticErrorIfNotNever(x.toString);
staticErrorIfNotNever(x.runtimeType);
staticErrorIfNotNever(x.noSuchMethod);
staticErrorIfNotNever(x.noSuchMethod(x));
staticErrorIfNotNever(x.hashCode);
staticErrorIfNotNever(x == x);
staticErrorIfNotNever(x == 3);
staticErrorIfNotNever(3 == x);
// ^^^^^^^^^^^^^^^^^
// [analyzer] COMPILE_TIME_ERROR.COULD_NOT_INFER
// [cfe] Inferred type argument 'bool' doesn't conform to the bound 'Never' of the type variable 'T' on 'staticErrorIfNotNever'.
}
}
extension NeverExt on Never {
int neverMethod() => 3;
}
extension ObjectExt on Object {
int objectMethod() => 3;
}
void extensionsDontApply(Never x) {
{
var t = x.neverMethod();
staticErrorIfNotNever(t);
}
{
var t = x.objectMethod();
staticErrorIfNotNever(t);
}
{
var t = NeverExt(x).neverMethod();
staticErrorIfNotNever(t);
// ^^^^^^^^^^^^^^^^^
// [analyzer] COMPILE_TIME_ERROR.COULD_NOT_INFER
// [cfe] Inferred type argument 'int' doesn't conform to the bound 'Never' of the type variable 'T' on 'staticErrorIfNotNever'.
}
{
var t = ObjectExt(x).objectMethod();
staticErrorIfNotNever(t);
// ^^^^^^^^^^^^^^^^^
// [analyzer] COMPILE_TIME_ERROR.COULD_NOT_INFER
// [cfe] Inferred type argument 'int' doesn't conform to the bound 'Never' of the type variable 'T' on 'staticErrorIfNotNever'.
}
}
void throwHasTypeNever() {
var t = throw "hello";
staticErrorIfNotNever(t);
}
void neverReturns(bool b) {
{
var f = () => throw "Unreachable";
var t = f();
staticErrorIfNotNever(t);
}
{
var f = () {
if (b) {
throw "argle";
} else {
throw "bargle";
}
};
var t = f();
staticErrorIfNotNever(t);
}
}
void main() {
neverHasAllMembers(throw "Unreachable");
extensionsDontApply(throw "Unreachable");
throwHasTypeNever();
neverReturns(true);
}
@@ -0,0 +1,29 @@
// Copyright (c) 2020, 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.
/// Test that the type of a local function is an error if local function type
/// inference requires the type of the function being inferred.
void main() {
f() {
return 3;
}
f().arglebargle;
// ^^^^^^^^^^^
// [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
// [cfe] The getter 'arglebargle' isn't defined for the class 'int'.
f().isEven; // Inferred type is int
g() {
if (f() == 3) {
return g();
} else {
return 3;
}
}
// ^
// [analyzer] undefined
// [cfe] undefined
}
@@ -0,0 +1,152 @@
// Copyright (c) 2020, 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.
/// Test that downwards inference uses the promoted type of the variable in an
/// assignment as the context, but still considers the assignment as a valid
/// promotion/demotion point.
/// A generic class to serve as the base type.
class C<S> {
S cMethod(S x) => x;
}
/// An inference context C<S> constrains the first type variable of D but not
/// the second.
class D<S, T> extends C<S> {
S dMethod1(S x) => x;
T dMethod2(T x) => x;
}
/// Generic function which if inferred in a context C<A> with argument type B,
/// should infer to mkD<A, B>
D<S, T> mkD<S, T>(T x) => D();
/// Generic function which if inferred in a context D<S0, S1> with argument type
/// D<T0, T1>, should infer to useD<T0>.
C<T> useD<T>(D<T, int> d) => d;
void main() {
{
C<String> x = C();
// Inference uses C<String> as a downwards context, constraining S from
// mkD<S,T> to String. Upwards inference constrains T to int. D<String,
// int> is not a type of interest so no promotion should happen.
{
// y has the type of the RHS of the assignment
var y = x = mkD(3);
// x still has type C<String>
x.dMethod1("hello");
//^^^^^^^^
// [analyzer] STATIC_TYPE_WARNING.UNDEFINED_METHOD
// [cfe] The method 'dMethod1' isn't defined for the class 'C<String>'.
x.dMethod2(3);
//^^^^^^^^
// [analyzer] STATIC_TYPE_WARNING.UNDEFINED_METHOD
// [cfe] The method 'dMethod2' isn't defined for the class 'C<String>'.
var t0 = x.cMethod("hello");
t0.length;
t0.arglebargle; // t0 is not dynamic
// ^^^^^^^^^^^
// [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
// [cfe] The getter 'arglebargle' isn't defined for the class 'String'.
// y has type D<String, int>
var t1 = y.dMethod1("hello");
t1.length;
t1.arglebargle; // t1 is not dynamic
// ^^^^^^^^^^^
// [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
// [cfe] The getter 'arglebargle' isn't defined for the class 'String'.
var t2 = y.dMethod2(3);
t2.isEven;
t2.arglebargle; // t2 is not dynamic
// ^^^^^^^^^^^
// [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
// [cfe] The getter 'arglebargle' isn't defined for the class 'int'.
}
// Establish D<String, int> as a type of interest
if (x is D<String, int>) {}
// Inference uses C<String> as a downwards context, constraining S from
// mkD<S,T> to String. Upwards inference constrains T to int. D<String,
// int> is a type of interest so promotion should happen.
{
// y has the type of the RHS of the assignment
var y = x = mkD(3);
// x has type D<String, int>
var t0 = x.dMethod1("hello");
t0.length;
t0.arglebargle; // t0 is not dynamic
// ^^^^^^^^^^^
// [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
// [cfe] The getter 'arglebargle' isn't defined for the class 'String'.
var t1 = x.dMethod2(3);
t1.isEven;
t1.arglebargle; // t1 is not dynamic
// ^^^^^^^^^^^
// [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
// [cfe] The getter 'arglebargle' isn't defined for the class 'int'.
// y has type D<String, int>
var t2 = y.dMethod1("hello");
t2.length;
t2.arglebargle; // t2 is not dynamic
// ^^^^^^^^^^^
// [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
// [cfe] The getter 'arglebargle' isn't defined for the class 'String'.
var t3 = y.dMethod2(3);
t3.isEven;
t3.arglebargle; // t3 is not dynamic
// ^^^^^^^^^^^
// [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
// [cfe] The getter 'arglebargle' isn't defined for the class 'int'.
}
// Inference should use D<String, int> as a downwards context, T from
// useD<T> to int. The variable x as an argument has type D<String, int>
// which is assignable to D<String, int> so the call should have no error.
// C<String> is a type of interest, so x should be demoted after the call.
{
// y has the type of the RHS of the assignment
var y = x = useD(x);
// x has type C<String>, and not D<String, int>
x.dMethod1("hello");
//^^^^^^^^
// [analyzer] STATIC_TYPE_WARNING.UNDEFINED_METHOD
// [cfe] The method 'dMethod1' isn't defined for the class 'C<String>'.
x.dMethod2(3);
//^^^^^^^^
// [analyzer] STATIC_TYPE_WARNING.UNDEFINED_METHOD
// [cfe] The method 'dMethod2' isn't defined for the class 'C<String>'.
var t0 = x.cMethod("hello");
t0.length;
t0.arglebargle; // t0 is not dynamic
// ^^^^^^^^^^^
// [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
// [cfe] The getter 'arglebargle' isn't defined for the class 'String'.
// C<String>, and not D<String, int>
y.dMethod1("hello");
//^^^^^^^^
// [analyzer] STATIC_TYPE_WARNING.UNDEFINED_METHOD
// [cfe] The method 'dMethod1' isn't defined for the class 'C<String>'.
y.dMethod2(3);
//^^^^^^^^
// [analyzer] STATIC_TYPE_WARNING.UNDEFINED_METHOD
// [cfe] The method 'dMethod2' isn't defined for the class 'C<String>'.
var t1 = y.cMethod("hello");
t1.length;
t1.arglebargle; // t0 is not dynamic
// ^^^^^^^^^^^
// [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
// [cfe] The getter 'arglebargle' isn't defined for the class 'String'.
}
}
}
@@ -0,0 +1,21 @@
// Copyright (c) 2020, 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.
// Requirements=nnbd-weak
/// Test that typedefs imported from opted out libraries are treated as
/// non-nullable at the top level, with legacy components.
import 'typedef_opted_out.dart';
int? takesNonNullable(int x) {}
void main() {
F f = null; // typedefs from opted out libraries are treated as non-nullable
// ^^^^
// [analyzer] STATIC_TYPE_WARNING.INVALID_ASSIGNMENT
// [cfe] A value of type 'Null' can't be assigned to a variable of type 'int Function(int)'.
f = takesNonNullable; // F is int* Function(int*)
f(null); // F is int* Function(int*)
}
@@ -0,0 +1,7 @@
// Copyright (c) 2020, 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.
// @dart=2.7
typedef F = int Function(int);