From 9076b6a76a5c4e7a933002c2e6b8e3d3f8fdf03f Mon Sep 17 00:00:00 2001 From: "Lasse R.H. Nielsen" Date: Fri, 3 Sep 2021 09:54:49 +0000 Subject: [PATCH] Add test for parsing explicit instantiations. Change-Id: Ia1c5b6b2365849a6c69b1db2bf1878ca64dfd65b Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/211420 Reviewed-by: Paul Berry Reviewed-by: Erik Ernst Commit-Queue: Lasse R.H. Nielsen --- ...licit_type_instantiation_parsing_test.dart | 321 ++++++++++++++++++ 1 file changed, 321 insertions(+) create mode 100644 tests/language/explicit_type_instantiation_parsing_test.dart diff --git a/tests/language/explicit_type_instantiation_parsing_test.dart b/tests/language/explicit_type_instantiation_parsing_test.dart new file mode 100644 index 00000000000..4397a2c1b00 --- /dev/null +++ b/tests/language/explicit_type_instantiation_parsing_test.dart @@ -0,0 +1,321 @@ +// Copyright (c) 2021, 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. + +// SharedOptions=--enable-experiment=constructor-tearoffs + +// Test parsing around ambiguities in grammar for explicit type instantiation. +// +// If an expression is followed by a `<` +// which is then followed by potential type arguments and a `>`, +// it is parsed as type arguments if the next token is one of +// `)`, `}`, `]`, `:`, `;`, `,`, `(`, `.`, `==` or `!=`. +// Otherwise it's (attempted) parsed as a `<` infix operator. +// This decision is made no matter whether what otherwise follows +// is valid for that choice. + +typedef X<_> = Class; + +typedef Z<_, __> = Class; + +const Object? v = null; +const dynamic d = null; + +class Class { + Class([_]); + Class.named([_]); + + static Class get instance => Class(); + + Class get value => this; + Class call([_]) => this; +} + +int f1([_]) => 0; +int f2([_]) => 0; +int f3([_]) => 0; + +// Type of the instantiation of the functions above. +typedef F = int Function([Object? _]); + +void expect1(T? a) {} +void expect2(Object? a, Object? b) {} +void expect3(Object? a, Object? b, Object? c) {} +void expect4(Object? a, Object? b, Object? c, Object? d) {} + +// Anything goes! +// We only care about parsing here, if it parses, +// all objects support all the operations. +extension on T { + T get self => this; + dynamic get any => null; + Object? operator *(_) => null; + Object? operator -(_) => null; + Object? operator <(_) => null; + Object? operator >(_) => null; + Object? operator >>(_) => null; + Object? operator >>>(_) => null; + Object? operator [](_) => null; + Object? call([_]) => null; + bool get asBool => true; + int get prop => 0; + set prop(int _) {} +} + +void main() { + Object? as = "gotcha!"; // Built-in identifier declared as variable. + + // Validly parsed as type instantiation. + // Continuation tokens are: `(`, `.`, `==` and `!=`. + expect1(Z(2)); + expect1(Z.named(2)); + expect1(Z.named); // constructor tear-off + expect1(Z == Class); + expect1(Z != Class); + // Stop tokens are `)`, `,`, `}`, `]`, `:` and `;`. + expect1(Z); + expect1(Z,); + expect1>({Z}); + expect1>([Z]); + expect1(v.asBool ? Z : int); + expect1>({Z: 1}); + { + Type _ = Z; + } + + // Validly parsed as generic function instantiation. + expect1(f2(1)); + expect1(f2.self); + expect1(f2.self()); + expect1(f2 == null); + expect1(f2 != null); + + expect1(f2); + expect1(f2,); + expect1>({f2}); + expect1>([f2]); + expect1(v.asBool ? f2 : ([_]) => 2); + expect1>({f2 : 2}); + { + F _ = f2; + } + + // Also works if ending in `>>` or `>>>` + expect1(Z>(2)); + expect1(Z>.named(2)); + expect1(Z>.named); // constructor tear-off + expect1(Z> == Class); + expect1(Z> != Class); + // Stop tokens are `)`, `,`, `}`, `]`, `:` and `;`. + expect1(Z>); + expect1(Z>,); + expect1>({Z>}); + expect1>([Z>]); + expect1(v.asBool ? Z> : int); + expect1>({Z> : 1}); + { + Type _ = Z>; + } + + // Validly parsed as generic function instantiation. + expect1(f2>(1)); + expect1(f2>.self); + expect1(f2>.self()); + expect1(f2> == null); + expect1(f2> != null); + + expect1(f2>); + expect1(f2>,); + expect1>({f2>}); + expect1>([f2>]); + expect1(v.asBool ? f2> : ([_]) => 2); + expect1>({f2> : 2}); + { + F _ = f2>; + } + + expect1(Z>>(2)); + expect1(Z>>.named(2)); + expect1(Z>>.named); // constructor tear-off + expect1(Z>> == Class); + expect1(Z>> != Class); + // Stop tokens are `)`, `,`, `}`, `]`, `:` and `;`. + expect1(Z>>); + expect1(Z>>,); + expect1>({Z>>}); + expect1>([Z>>]); + expect1(v.asBool ? Z>> : int); + expect1>({Z>>: 1}); + { + Type _ = Z>>; + } + + // Validly parsed as generic function instantiation. + expect1(f2>>(1)); + expect1(f2>>.self); + expect1(f2>>.self()); + expect1(f2>> == null); + expect1(f2>> != null); + + expect1(f2>>); + expect1(f2>>,); + expect1>({f2>>}); + expect1>([f2>>]); + expect1(v.asBool ? f2>> : ([_]) => 2); + expect1>({f2>> : 2}); + { + F _ = f2>>; + } + + // Parsed as instantiation, can't access statics on instantiated type literal. + expect1(Z.instance); + // ^^^^^^^^ + // [cfe] Cannot access static member on an instantiated generic class. + // [analyzer] unspecified + + + // Not valid inside `<..>`, so always parsed as operators. + // The expect2 function requires two arguments, so it would be a type + // error to parse as type arguments. + expect2(Z < X, 2 > (2)); + expect2(Z < 2, X > (2)); + expect2(Z < X, 2 > (2)); + expect2(Z < X, v! > (2)); + expect3(Z < X, Z < X, 2 >> (2)); + expect4(Z < X, Z < X, Z < X, 2 >>> (2)); + // `as` is a built-in identifier, so it cannot be a *type*, + // preventing the lookahead from `<` from matching , + // and therefore it's parsed as an operator. + expect2(Z < X, as > (2)); + expect3(Z < X, Z < X, as >> (2)); + expect4(Z < X, Z < X, Z < X, as >>> (2)); + + // Validly parsed as operators due to disambiguation. + expect2(Z < X, X > X); + expect2(Z < X, X > 2); + expect2(Z < X, X > .2); // That `.` is part of the number literal, not a `.` token. + expect2(Z < X, X > -2); + expect2(Z < X, X > as); + expect2(Z < X, X > [1]); + expect2(Z < X, X > ![1].asBool); + expect2(Z < X, X > ++[1].prop); + expect2(Z < X, X > [1]); + + // Some would be valid as instantiation too, as proven by parenthefication. + expect1((Z) - 2); + expect1((Z)[1]); + expect1((Z)![1].asBool); // ignore: unnecessary_non_null_assertion + // ^ + // [cfe] Operand of null-aware operation '!' has type 'Type' which excludes null. + + // Works if the type argument would end in `>>` or `>>>` too. + expect3(Z < X, Z < X, X >> X); + expect3(Z < X, Z < X, X >> 2); + expect3(Z < X, Z < X, X >> .2); + expect3(Z < X, Z < X, X >> -2); + expect3(Z < X, Z < X, X >> as); + expect3(Z < X, Z < X, X >> [1]); + expect3(Z < X, Z < X, X >> ![1].asBool); + expect3(Z < X, Z < X, X >> ++[1].prop); + + expect4(Z < X, Z < X, Z < X, X >>> X); + expect4(Z < X, Z < X, Z < X, X >>> 2); + expect4(Z < X, Z < X, Z < X, X >>> .2); + expect4(Z < X, Z < X, Z < X, X >>> -2); + expect4(Z < X, Z < X, Z < X, X >>> as); + expect4(Z < X, Z < X, Z < X, X >>> [1]); + expect4(Z < X, Z < X, Z < X, X >>> ![1].asBool); + expect4(Z < X, Z < X, Z < X, X >>> ++[1].prop); + + // No valid parsing either way. + + // Content of type arguments not valid types. + // Cannot parse as operators since grammar doesn't allow chaining. + X<2>(2); + // ^ + // [cfe] A comparison expression can't be an operand of another comparison expression. + // [analyzer] SYNTACTIC_ERROR.EQUALITY_CANNOT_BE_EQUALITY_OPERAND + + X<2>; + // ^ + // [cfe] Expected an identifier, but got ';'. + // [analyzer] SYNTACTIC_ERROR.MISSING_IDENTIFIER + // ^ + // [cfe] A comparison expression can't be an operand of another comparison expression. + // [analyzer] SYNTACTIC_ERROR.EQUALITY_CANNOT_BE_EQUALITY_OPERAND + + X<2>.instance; // Not type argument. + // ^ + // [cfe] Expected an identifier, but got '.'. + // [analyzer] SYNTACTIC_ERROR.MISSING_IDENTIFIER + // ^ + // [cfe] A comparison expression can't be an operand of another comparison expression. + // [analyzer] SYNTACTIC_ERROR.EQUALITY_CANNOT_BE_EQUALITY_OPERAND + + X<2>.any; + // ^ + // [cfe] Expected an identifier, but got '.'. + // [analyzer] SYNTACTIC_ERROR.MISSING_IDENTIFIER + // ^ + // [cfe] A comparison expression can't be an operand of another comparison expression. + // [analyzer] SYNTACTIC_ERROR.EQUALITY_CANNOT_BE_EQUALITY_OPERAND + + // This would be invalid even if `X` had an `any` member. See next. + X.any; // Invalid, Class does not have any static `any` member. + // ^^^ + // [cfe] Member not found: 'any'. + // [analyzer] unspecified + + X.instance; // Does have static `instance` member, can't access this way. + // ^^^^^^^^ + // [cfe] Cannot access static member on an instantiated generic class. + // [analyzer] unspecified + + // Parse error. + + X2; + // ^ + // [cfe] A comparison expression can't be an operand of another comparison expression. + // [analyzer] SYNTACTIC_ERROR.EQUALITY_CANNOT_BE_EQUALITY_OPERAND + + // Doesn't parse as operators, would be valid if type arguments. + + // The following `-` forces operators, but those can't parse like this. + X-1; + // ^ + // [cfe] A comparison expression can't be an operand of another comparison expression. + // [analyzer] SYNTACTIC_ERROR.EQUALITY_CANNOT_BE_EQUALITY_OPERAND + + // Parsed as operators on function instantiation too (parsing doesn't know.) + f1 - 1; + // ^ + // [cfe] A comparison expression can't be an operand of another comparison expression. + // [analyzer] SYNTACTIC_ERROR.EQUALITY_CANNOT_BE_EQUALITY_OPERAND + + // Parsed as a generic instantiation, but `v` is not generic function or type. + // Would be valid if parsed as operators. + expect1(v < X, X > (2)); + // ^^^^^^^^ + // [cfe] unspecified + // [analyzer] unspecified + + // Parsed as a generic instantiation, but `d` is not generic function or type. + // Being dynamic doesn't help. + // Would be valid if parsed as operators. + expect1(v < X, X > (2)); + // ^^^^^^^^ + // [cfe] unspecified + // [analyzer] unspecified + + // Valid only if parenthesized. + expect1((Z < X, X >) * 2); + + // Valid only if parenthesized. + expect1((Z < X, X >) < 4); + + // Still can't instantiate something non-generic. + /**/ v; + // ^^^^^ + // [cfe] The static type of the explicit instantiation operand must be a generic function type but is 'Object?'. + // [analyzer] unspecified +}