From 44b0733a6927fa95d6608f12140ed9f4fbb53bfb Mon Sep 17 00:00:00 2001 From: "Sergey G. Grekhov" Date: Fri, 6 Dec 2024 12:18:01 +0200 Subject: [PATCH] #2976. Add type inference tests. Part 2 (#3005) Add type inference tests. Part 2 --- .../grammar_A03_t02.dart | 46 ++++++++++++ .../semantics_A05_t01.dart | 71 +++++++++++++++++++ .../type_inference_A01_t07.dart | 42 +++++++++++ .../type_inference_A06_t02.dart | 39 ++++++++++ .../type_inference_A07_t01.dart | 56 +++++++++++++++ .../type_inference_A07_t02.dart | 37 ++++++++++ .../type_inference_A08_t01.dart | 67 +++++++++++++++++ .../type_inference_A08_t02.dart | 67 +++++++++++++++++ 8 files changed, 425 insertions(+) create mode 100644 LanguageFeatures/Static-access-shorthand/grammar_A03_t02.dart create mode 100644 LanguageFeatures/Static-access-shorthand/semantics_A05_t01.dart create mode 100644 LanguageFeatures/Static-access-shorthand/type_inference_A01_t07.dart create mode 100644 LanguageFeatures/Static-access-shorthand/type_inference_A06_t02.dart create mode 100644 LanguageFeatures/Static-access-shorthand/type_inference_A07_t01.dart create mode 100644 LanguageFeatures/Static-access-shorthand/type_inference_A07_t02.dart create mode 100644 LanguageFeatures/Static-access-shorthand/type_inference_A08_t01.dart create mode 100644 LanguageFeatures/Static-access-shorthand/type_inference_A08_t02.dart diff --git a/LanguageFeatures/Static-access-shorthand/grammar_A03_t02.dart b/LanguageFeatures/Static-access-shorthand/grammar_A03_t02.dart new file mode 100644 index 0000000000..16c6af3e42 --- /dev/null +++ b/LanguageFeatures/Static-access-shorthand/grammar_A03_t02.dart @@ -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 sgrekhov22@gmail.com + +// 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 +} diff --git a/LanguageFeatures/Static-access-shorthand/semantics_A05_t01.dart b/LanguageFeatures/Static-access-shorthand/semantics_A05_t01.dart new file mode 100644 index 0000000000..894e4d264d --- /dev/null +++ b/LanguageFeatures/Static-access-shorthand/semantics_A05_t01.dart @@ -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(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 `` instantiation and then an `(e2)` invocation. The context type of +/// that entire expression is used throughout the inference, where +/// `(e1.id)(e2)` has `(e1.id)` 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`, +/// `.id(args)`, `.id(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 ``. +/// +/// @description Checks that a named or unnamed constructor can be torn-off +/// using a shorthand syntax. +/// @author sgrekhov22@gmail.com + +// SharedOptions=--enable-experiment=enum-shorthands + +import '../../Utils/expect.dart'; + +class C { + T t; + C(this.t); + C.id(this.t); +} + +extension type ET(T t) { + ET.id(this.t); +} + +main() { + Object? o = C(0); + if (o is C) { + 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(0); + if (o is ET) { + o = .new; + if (o is Function) { + Expect.equals(3, o(3)); + } + } + o = ET(0); + if (o is ET) { + o = .id; + if (o is Function) { + Expect.equals(4, o(4)); + } + } +} diff --git a/LanguageFeatures/Static-access-shorthand/type_inference_A01_t07.dart b/LanguageFeatures/Static-access-shorthand/type_inference_A01_t07.dart new file mode 100644 index 0000000000..aef91411f7 --- /dev/null +++ b/LanguageFeatures/Static-access-shorthand/type_inference_A01_t07.dart @@ -0,0 +1,42 @@ +// 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 Declaration denoted by a type scheme A context type scheme is +/// said to denote a declaration in some cases. Not all context type schemes +/// denote a declaration. +/// If a type scheme `S`: +/// - has the form `C` or `C` where `C` is a type introduced by a +/// declaration `D` which must therefore be a type-introducing declaration, +/// which currently means a class, mixin, enum or extension type declaration, +/// then `S` denotes the declaration `D`. +/// - has the form `S?` or `FutureOr`, and the type scheme S denotes a +/// declaration D, then so does `S?/FutureOr`. Only the "base type" of the +/// union type is considered, ensuring that a type scheme denotes at most one +/// declaration or static namespace. +/// - has any other form, including type variables, promoted type variables and +/// `_`, then the type scheme does not denote any declaration or namespace. +/// +/// @description Checks that in case of context type `_` the type scheme does +/// not denote any declaration and it is a compile-time error to use a shorthand +/// in this case. +/// @author sgrekhov22@gmail.com + +// SharedOptions=--enable-experiment=enum-shorthands + +main() { + int v1 = (.parse("42")).abs(); +// ^ +// [analyzer] unspecified +// [cfe] unspecified + + int v2 = .parse("42") + 1; +// ^ +// [analyzer] unspecified +// [cfe] unspecified + + dynamic v3 = .parse("42"); +// ^ +// [analyzer] unspecified +// [cfe] unspecified +} diff --git a/LanguageFeatures/Static-access-shorthand/type_inference_A06_t02.dart b/LanguageFeatures/Static-access-shorthand/type_inference_A06_t02.dart new file mode 100644 index 0000000000..239923a853 --- /dev/null +++ b/LanguageFeatures/Static-access-shorthand/type_inference_A06_t02.dart @@ -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(args)` or +/// `.new` (as a prefix of a ` *` +/// production, or the entire chain) are compile-time errors, just like the +/// corresponding `T.new(args)` and `T.new` 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`. +/// @author sgrekhov22@gmail.com + +// SharedOptions=--enable-experiment=enum-shorthands + +class C { + C(); +} + +extension type ET(T v) {} + +main() { + Object? o = C(); + if (o is C) { + o = .new; +// ^ +// [analyzer] unspecified +// [cfe] unspecified + } + + o = ET(0); + if (o is ET) { + o = .new; +// ^ +// [analyzer] unspecified +// [cfe] unspecified + } +} diff --git a/LanguageFeatures/Static-access-shorthand/type_inference_A07_t01.dart b/LanguageFeatures/Static-access-shorthand/type_inference_A07_t01.dart new file mode 100644 index 0000000000..784547dc15 --- /dev/null +++ b/LanguageFeatures/Static-access-shorthand/type_inference_A07_t01.dart @@ -0,0 +1,56 @@ +// 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 l = .filled(10, 10);` works like doing +/// `List l = List.filled(10, 10);`, and it is the following downwards +/// inference with context type `List` that makes it into +/// `List.filled(10, 10);`. This distinction matters for something like: +/// ``` +/// List 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`. 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 in case of a constructor invocation the type +/// inference occurs as if the shorthand expression is preceded by the raw type +/// and then a type inference infers the type arguments. +/// @author sgrekhov22@gmail.com + +// SharedOptions=--enable-experiment=enum-shorthands + +import '../../Utils/expect.dart'; + +class C { + T value; + C(this.value); + C.id(this.value); + + C toInt(int v) => C(v); +} + +extension type ET(T v) { + ET.id(this.v); + + ET toInt(int v) => ET(v); +} + +main() { + C c1 = .new("String").toInt(1); + Expect.equals(1, c1.value); + + C c2 = .id("String").toInt(2); + Expect.equals(1, c2.value); + + ET et1 = .new("String").toInt(3); + Expect.equals(3, et1.v); + + ET et2 = .id("String").toInt(4); + Expect.equals(4, et2.v); +} diff --git a/LanguageFeatures/Static-access-shorthand/type_inference_A07_t02.dart b/LanguageFeatures/Static-access-shorthand/type_inference_A07_t02.dart new file mode 100644 index 0000000000..0a2b19a3e5 --- /dev/null +++ b/LanguageFeatures/Static-access-shorthand/type_inference_A07_t02.dart @@ -0,0 +1,37 @@ +// 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 l = .filled(10, 10);` works like doing +/// `List l = List.filled(10, 10);`, and it is the following downwards +/// inference with context type `List` that makes it into +/// `List.filled(10, 10);`. This distinction matters for something like: +/// ``` +/// List 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`. 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 in case of a constructor invocation the type +/// inference occurs as if the shorthand expression is preceded by the raw type +/// and then a type inference infers the type arguments. +/// @author sgrekhov22@gmail.com + +// SharedOptions=--enable-experiment=enum-shorthands + +import '../../Utils/expect.dart'; + +main() { + List l = + .generate(10, (int i) => i + 1).map((x) => x.toRadixString(16)).toList(); + Expect.listEquals(["1", "2", "3", "4", "5", "6", "7", "8", "9", "a"], l); + + num n = .parse("42").abs(); + Expect.equals(42, n); +} diff --git a/LanguageFeatures/Static-access-shorthand/type_inference_A08_t01.dart b/LanguageFeatures/Static-access-shorthand/type_inference_A08_t01.dart new file mode 100644 index 0000000000..f8b4477633 --- /dev/null +++ b/LanguageFeatures/Static-access-shorthand/type_inference_A08_t01.dart @@ -0,0 +1,67 @@ +// 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 `!` selector propagates the context type in case of +/// shorthand expression. +/// @author sgrekhov22@gmail.com + +// 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); + + c = (.id)!; + Expect.equals(0, c.value); + + M m = .id!; + Expect.equals(1, m.value); + + m = (.id)!; + Expect.equals(1, m.value); + + E e = .id!; + Expect.equals(2, e.value); + + e = (.id)!; + Expect.equals(2, e.value); + + ET et = .id!; + Expect.equals(3, et.v); + + et = (.id)!; + Expect.equals(3, et.v); +} diff --git a/LanguageFeatures/Static-access-shorthand/type_inference_A08_t02.dart b/LanguageFeatures/Static-access-shorthand/type_inference_A08_t02.dart new file mode 100644 index 0000000000..941e4a78a6 --- /dev/null +++ b/LanguageFeatures/Static-access-shorthand/type_inference_A08_t02.dart @@ -0,0 +1,67 @@ +// 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 `??` operator propagates the context type in case of +/// shorthand expression. +/// @author sgrekhov22@gmail.com + +// 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); + + c = (.id) ?? C(-1); + Expect.equals(0, c.value); + + M m = .id ?? MC(-1); + Expect.equals(1, m.value); + + m = (.id) ?? MC(-1); + Expect.equals(1, m.value); + + E e = .id ?? E.e1; + Expect.equals(2, e.value); + + e = (.id) ?? E.e1; + Expect.equals(2, e.value); + + ET et = .id ?? ET(-1); + Expect.equals(3, et.v); + + et = .id ?? ET(-1); + Expect.equals(3, et.v); +}