Skip to content

Commit

Permalink
dart-lang#2976. Add grammar, semantics and type inference test for co…
Browse files Browse the repository at this point in the history
…nstants
  • Loading branch information
sgrekhov committed Dec 2, 2024
1 parent 2d721bd commit e4cd732
Show file tree
Hide file tree
Showing 7 changed files with 393 additions and 1 deletion.
65 changes: 65 additions & 0 deletions LanguageFeatures/Static-access-shorthand/grammar_A01_t06.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// 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 introduce grammar productions of the form:
/// ```
/// <postfixExpression> ::= ... -- all current productions
/// | <staticMemberShorthand>
///
/// <constantPattern> ::= ... -- all current productions
/// | <staticMemberShorthand>
///
/// <staticMemberShorthand> ::= <staticMemberShorthandHead> <selector*>
///
/// <staticMemberShorthandHead> ::=
/// '.' (<identifier> | 'new') -- shorthand qualified name
/// | 'const' '.' (<identifier> | 'new') <arguments> -- shorthand object creation
/// ```
///
/// @description Checks that `<staticMemberShorthand>` can be used in a constant
/// pattern.
/// @author [email protected]
// SharedOptions=--enable-experiment=enum-shorthands

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

class C {
final String value;
const C(this.value);
const C.foo(this.value);
}

extension type const ET(int v) {
const ET.foo(this.v);
}

main() {
bool success = false;
var c1 = const <C>[.new("c1")];
if (c1 case const <C>[.new("c1")]) {
success = true;
}
Expect.isTrue(success);

success = false;
var c2 = const <C>[.foo("c2")];
if (c2 case const <C>[const .foo("c2")]) {
success = true;
}
Expect.isTrue(success);

var et1 = const <ET>[.new(1)];
if (et1 case const <ET>[const .new(1)]) {
success = true;
}
Expect.isTrue(success);

success = false;
var et2 = const <ET>[.foo(2)];
if (et2 case const <ET>[.foo(2)]) {
success = true;
}
Expect.isTrue(success);
}
95 changes: 95 additions & 0 deletions LanguageFeatures/Static-access-shorthand/semantics_A04_t03.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
// 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 `staticMemberShorthand` invokes correct static
/// member depending on the context type.
/// @author [email protected]
// SharedOptions=--enable-experiment=enum-shorthands

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

class A {
String log = "";

A() {
log = "A.new";
}
A.id() {
log = "A.id";
}
}

class C extends A {
C() {
log = "C.new";
}
C.id() {
log = "C.id";
}
factory C.f1() => .new();
factory C.f2() => .id();
}

class D extends C {
D() {
log = "D.new";
}
D.id() {
log = "D.id";
}
}

A foo1() => .new();
A foo2() => .id();

C bar1() => .new();
C bar2() => .id();

D baz1() => .new();
D baz2() => .id();

main() {
var c1 = C.f1();
Expect.equals("C.new", c1.log);

var c2 = C.f2();
Expect.equals("C.id", c2.log);

var a1 = foo1();
Expect.equals("A.new", a1.log);

var a2 = foo2();
Expect.equals("A.id", a2.log);

var c3 = bar1();
Expect.equals("C.new", c3.log);

var c4 = bar2();
Expect.equals("C.id", c4.log);

var d1 = baz1();
Expect.equals("D.new", d1.log);

var d2 = baz2();
Expect.equals("D.id", d2.log);
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,4 @@ main() {
// ^
// [analyzer] unspecified
// [cfe] unspecified

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// 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 When inferring types for a `const .id(arguments)` or
/// `const .new(arguments)` with context type schema `C`, let `D` be the
/// declaration denoted by the shorthand context assigned to the
/// `<staticMemberShorthand>`. Then proceed with type inference as if `.id/.new`
/// was preceded by an identifier `D` denoting the declaration `D`. It’s a
/// compile-time error if the shorthand context does not denote a class, mixin,
/// enum or extension type declaration, or if `D.id/D.new` does not denote a
/// constant constructor.
///
/// @description Checks that if a shorthand context type schema has one of the
/// forms `const .id(arguments)`, or `const .new(arguments)`, `D` is the
/// declaration denoted by the shorthand context then then the shorthand context
/// denotes the type declaration `D`. Test a class.
/// @author [email protected]
// SharedOptions=--enable-experiment=enum-shorthands

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

class C<X> {
final Type type;
const C() : type = X;
const C.id(int _) : type = X;
}

typedef NullableInt = int?;
typedef NullableString = String?;

main() {
C c1 = const .new();
Expect.equals(dynamic, c1.type);

C<bool> c2 = const .new();
Expect.equals(bool, c2.type);

C? c3 = const .new();
Expect.equals(dynamic, c3.type);

C<int?> c4 = const .new();
Expect.equals(NullableInt, c4.type);

C<int>? c5 = const .new();
Expect.equals(int, c5.type);

C<String?>? c6 = const .new();
Expect.equals(NullableString, c6.type);

C c7 = const .id(0);
Expect.equals(dynamic, c7.type);

C<bool> c8 = const .id(0);
Expect.equals(bool, c8.type);

C? c9 = const .id(0);
Expect.equals(dynamic, c9.type);

C<int?> c10 = const .id(0);
Expect.equals(NullableInt, c10.type);

C<int>? c11 = const .id(0);
Expect.equals(int, c11.type);

C<String?>? c12 = const .id(0);
Expect.equals(NullableString, c12.type);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// 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 When inferring types for a `const .id(arguments)` or
/// `const .new(arguments)` with context type schema `C`, let `D` be the
/// declaration denoted by the shorthand context assigned to the
/// `<staticMemberShorthand>`. Then proceed with type inference as if `.id/.new`
/// was preceded by an identifier `D` denoting the declaration `D`. It’s a
/// compile-time error if the shorthand context does not denote a class, mixin,
/// enum or extension type declaration, or if `D.id/D.new` does not denote a
/// constant constructor.
///
/// @description Checks that if a shorthand context type schema has one of the
/// forms `const .id(arguments)`, or `const .new(arguments)`, `D` is the
/// declaration denoted by the shorthand context then then the shorthand context
/// denotes the type declaration `D`. Test an extension type.
/// @author [email protected]
// SharedOptions=--enable-experiment=enum-shorthands

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

extension type const ET<X>.foo(Type type) {
const ET.new() : type = X;
const ET.id() : type = X;
}

typedef NullableInt = int?;
typedef NullableString = String?;

main() {
ET et1 = const .new();
Expect.equals(dynamic, et1.type);

ET<bool> et2 = const .new();
Expect.equals(bool, et2.type);

ET? et3 = const .new();
Expect.equals(dynamic, et3.type);

ET<int?> et4 = const .new();
Expect.equals(NullableInt, et4.type);

ET<int>? et5 = const .new();
Expect.equals(int, et5.type);

ET<String?>? et6 = const .new();
Expect.equals(NullableString, et6.type);

ET et7 = const .id();
Expect.equals(dynamic, et7.type);

ET<bool> et8 = const .id();
Expect.equals(bool, et8.type);

ET? et9 = const .id();
Expect.equals(dynamic, et9.type);

ET<int?> et10 = const .id();
Expect.equals(NullableInt, et10.type);

ET<int>? et11 = const .id();
Expect.equals(int, et11.type);

ET<String?>? et12 = const .id();
Expect.equals(NullableString, et12.type);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// 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 When inferring types for a `const .id(arguments)` or
/// `const .new(arguments)` with context type schema `C`, let `D` be the
/// declaration denoted by the shorthand context assigned to the
/// `<staticMemberShorthand>`. Then proceed with type inference as if `.id/.new`
/// was preceded by an identifier `D` denoting the declaration `D`. It’s a
/// compile-time error if the shorthand context does not denote a class, mixin,
/// enum or extension type declaration, or if `D.id/D.new` does not denote a
/// constant constructor.
///
/// @description Checks that it’s a compile-time error if the constant shorthand
/// context does not denote a class, mixin, enum or extension type declaration.
/// @author [email protected]
// SharedOptions=--enable-experiment=enum-shorthands


main() {
var v1 = const .new();
// ^
// [analyzer] unspecified
// [cfe] unspecified

final v2 = const .new(2);
// ^
// [analyzer] unspecified
// [cfe] unspecified

const v3 = const .id(3);
// ^
// [analyzer] unspecified
// [cfe] unspecified
}
Loading

0 comments on commit e4cd732

Please sign in to comment.