Skip to content

Commit

Permalink
dart-lang#2976. Add type inference tests. Part 2
Browse files Browse the repository at this point in the history
  • Loading branch information
sgrekhov committed Dec 5, 2024
1 parent 2d1547e commit da5b568
Show file tree
Hide file tree
Showing 8 changed files with 381 additions and 0 deletions.
46 changes: 46 additions & 0 deletions LanguageFeatures/Static-access-shorthand/grammar_A03_t02.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// Copyright (c) 2024, 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.

/// @assertion We also add . to the tokens that an expression statement cannot
/// start with. This doesn't affect starting with a double literal like `.42`,
/// since that's a different token than a single `.`.
///
/// @description Checks that it is a compile-time error if an expression
/// statement is started with `.`.
/// @author [email protected]
// SharedOptions=--enable-experiment=enum-shorthands

void foo() {}

class C {}

enum E {e0;}

main() {
.new;
//^
// [analyzer] unspecified
// [cfe] unspecified

.new();
//^
// [analyzer] unspecified
// [cfe] unspecified

.C();
//^
// [analyzer] unspecified
// [cfe] unspecified

.foo();
//^
// [analyzer] unspecified
// [cfe] unspecified

.e0;
//^
// [analyzer] unspecified
// [cfe] unspecified
}
71 changes: 71 additions & 0 deletions LanguageFeatures/Static-access-shorthand/semantics_A05_t01.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// Copyright (c) 2024, 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.

/// @assertion Dart semantics, static and dynamic, do not follow the grammar
/// precisely. For example, a static member invocation expression of the form
/// `C.id<T1>(e2)` is treated as an atomic entity for type inference (and
/// runtime semantics). It’s not a combination of doing a `C.id` tear-off, then
/// a `<T1>` instantiation and then an `(e2)` invocation. The context type of
/// that entire expression is used throughout the inference, where
/// `(e1.id<T1>)(e2)` has `(e1.id<T1>)` in a position where it has no context
/// type.
///
/// Because of that, the specification of the static and runtime semantics of
/// the new constructs needs to address all the forms `.id`, `.id<typeArgs>`,
/// `.id(args)`, `.id<typeArgs>(args)`, `.new` or `.new(args)`.
/// ...
/// The general rule is that any of the expression forms above, starting with
/// `.id`, are treated exactly as if they were preceded by a fresh prefixed
/// identifier `_p.C` which denotes the declaration of the type of the context
/// type scheme of the entire `<staticMemberShorthand>`.
///
/// @description Checks that a named or unnamed constructor can be torn-off
/// using a shorthand syntax.
/// @author [email protected]
// SharedOptions=--enable-experiment=enum-shorthands

import '../../Utils/expect.dart';

class C<T> {
T t;
C(this.t);
C.id(this.t);
}

extension type ET<T>(T t) {
ET.id(this.t);
}

main() {
Object? o = C<int>(0);
if (o is C<int>) {
o = .new;
if (o is Function) {
Expect.equals(1, o(1).t);
}
}
o = C(0);
if (o is C) {
o = .id;
if (o is Function) {
Expect.equals(2, o(2).t);
}
}

o = ET<int>(0);
if (o is ET) {
o = .new;
if (o is Function) {
Expect.equals(3, o(3));
}
}
o = ET<int>(0);
if (o is ET) {
o = .id;
if (o is Function) {
Expect.equals(4, o(4));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,9 @@ main() {
// [analyzer] unspecified
// [cfe] unspecified
}.toList();

dynamic v = .parse("42");
// ^
// [analyzer] unspecified
// [cfe] unspecified
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// Copyright (c) 2024, 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.

/// @assertion Expressions of the forms `.new<typeArgs>(args)` or
/// `.new<typeArgs>` (as a prefix of a `<staticMemberShorthand> <selector>*`
/// production, or the entire chain) are compile-time errors, just like the
/// corresponding `T.new<typeArgs>(args)` and `T.new<typeArgs>` already are,
/// whether used as instantiated tear-off or invoked.
///
/// @description Checks that it is a compile-time error to use a shorthand
/// expression of the form `.new<typeArgs>`.
/// @author [email protected]
// SharedOptions=--enable-experiment=enum-shorthands

class C<T> {
C();
}

extension type ET<T>(T v) {}

main() {
Object? o = C();
if (o is C) {
o = .new<int>;
// ^
// [analyzer] unspecified
// [cfe] unspecified
}

o = ET(0);
if (o is ET) {
o = .new<int>;
// ^
// [analyzer] unspecified
// [cfe] unspecified
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// Copyright (c) 2024, 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.

/// @assertion The invocation of a constructor is not using an instantiated
/// type, it’s behaving as if the constructor was preceded by a raw type, which
/// type inference should then infer type arguments for. Doing
/// `List<int> l = .filled(10, 10);` works like doing
/// `List<int> l = List.filled(10, 10);`, and it is the following downwards
/// inference with context type `List<int>` that makes it into
/// `List<int>.filled(10, 10);`. This distinction matters for something like:
/// ```
/// List<String> l =
/// .generate(10, (int i) => i + 1).map((x) => x.toRadixString(16)).toList();
/// ```
/// which is equivalent to inserting `List` in front of `.generate`, which will
/// then be inferred as `List<int>`. In most normal use cases it doesn’t matter,
/// because the context type will fill in the missing type variables, but if the
/// construction is followed by more selectors, it loses that context type.
///
/// @description Checks that it is a compile-time error to use a shorthand
/// expression if additional selectors strip its context type.
/// @author [email protected]
// SharedOptions=--enable-experiment=enum-shorthands

class C<T> {
T value;
C(this.value);
C.id(this.value);

C<int> toInt(int v) => C<int>(v);
}

extension type ET<T>(T v) {
ET.id(this.v);

ET<int> toInt(int v) => ET<int>(v);
}

main() {
C<int> c1 = .new("String").toInt(0);
// ^
// [analyzer] unspecified
// [cfe] unspecified

C<int> c2 = .id("String").toInt(0);
// ^
// [analyzer] unspecified
// [cfe] unspecified

ET<int> et1 = .new("String").toInt(0);
// ^
// [analyzer] unspecified
// [cfe] unspecified

ET<int> et2 = .id("String").toInt(0);
// ^
// [analyzer] unspecified
// [cfe] unspecified

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// Copyright (c) 2024, 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.

/// @assertion The invocation of a constructor is not using an instantiated
/// type, it’s behaving as if the constructor was preceded by a raw type, which
/// type inference should then infer type arguments for. Doing
/// `List<int> l = .filled(10, 10);` works like doing
/// `List<int> l = List.filled(10, 10);`, and it is the following downwards
/// inference with context type `List<int>` that makes it into
/// `List<int>.filled(10, 10);`. This distinction matters for something like:
/// ```
/// List<String> l =
/// .generate(10, (int i) => i + 1).map((x) => x.toRadixString(16)).toList();
/// ```
/// which is equivalent to inserting `List` in front of `.generate`, which will
/// then be inferred as `List<int>`. In most normal use cases it doesn’t matter,
/// because the context type will fill in the missing type variables, but if the
/// construction is followed by more selectors, it loses that context type.
///
/// @description Checks that it is a compile-time error to use a shorthand
/// expression if additional selectors strip its context type.
/// @author [email protected]
// SharedOptions=--enable-experiment=enum-shorthands

main() {
List<String> l =
.generate(10, (int i) => i + 1).map((x) => x.toRadixString(16)).toList();
// ^
// [analyzer] unspecified
// [cfe] unspecified

num n = .parse("42").abs();
// ^
// [analyzer] unspecified
// [cfe] unspecified

int i = (.parse("42")).abs();
// ^
// [analyzer] unspecified
// [cfe] unspecified

int v = .parse("42") + 1;
// ^
// [analyzer] unspecified
// [cfe] unspecified
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// Copyright (c) 2024, 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.

/// @assertion Since `!` does propagate a context,
/// `int x = (.tryParse(input))!;` does work, with a context type scheme of
/// `int?`, which is enough to allow `.tryParse`. Same for
/// `int x = .tryParse(input) ?? 0;` which gives the first operand the context
/// type `int?`.
///
/// @description Checks that if shorthand expression is followed by `!` then its
/// original context type is preserved.
/// @author [email protected]
// SharedOptions=--enable-experiment=enum-shorthands

import '../../Utils/expect.dart';

class C {
int value;
C(this.value);

static C? get id => C(0);
}

mixin M on C {
static M? get id => MC(1);
}
class MC = C with M;

enum E {
e0(2);
final int value;
const E(this.value);

static E? get id => E.e0;
}

extension type ET(int v) {
static ET get id => ET(3);
}

main() {
C c = .id!;
Expect.equals(0, c.value);

M m = .id!;
Expect.equals(1, m.value);

E e = .id!;
Expect.equals(2, e.value);

ET et = .id!;
Expect.equals(3, et.v);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// Copyright (c) 2024, 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.

/// @assertion Since `!` does propagate a context,
/// `int x = (.tryParse(input))!;` does work, with a context type scheme of
/// `int?`, which is enough to allow `.tryParse`. Same for
/// `int x = .tryParse(input) ?? 0;` which gives the first operand the context
/// type `int?`.
///
/// @description Checks that if shorthand expression is followed by `??` then
/// its original context type is preserved.
/// @author [email protected]
// SharedOptions=--enable-experiment=enum-shorthands

import '../../Utils/expect.dart';

class C {
int value;
C(this.value);

static C? get id => C(0);
}

mixin M on C {
static M? get id => MC(1);
}
class MC = C with M;

enum E {
e0(2), e1(-1);
final int value;
const E(this.value);

static E? get id => E.e0;
}

extension type ET(int v) {
static ET get id => ET(3);
}

main() {
C c = .id ?? C(-1);
Expect.equals(0, c.value);

M m = .id ?? MC(-1);
Expect.equals(1, m.value);

E e = .id ?? E.e1;
Expect.equals(2, e.value);

ET et = .id ?? ET(-1);
Expect.equals(3, et.v);
}

0 comments on commit da5b568

Please sign in to comment.