mirror of
https://github.com/dart-lang/sdk
synced 2024-10-14 08:51:21 +00:00
Support type variables on function types and intersection types
Change-Id: Ic4f6f26f1e779e9fc084a06f546584f826a6e720 Reviewed-on: https://dart-review.googlesource.com/c/90382 Reviewed-by: Dmitry Stefantsov <dmitryas@google.com>
This commit is contained in:
parent
f822d0abb8
commit
977e5b31e2
|
@ -10,6 +10,7 @@ import "package:kernel/ast.dart"
|
|||
FunctionType,
|
||||
InterfaceType,
|
||||
Library,
|
||||
NamedType,
|
||||
Node,
|
||||
Supertype,
|
||||
TreeNode,
|
||||
|
@ -24,6 +25,7 @@ import "type_parser.dart" as type_parser show parse;
|
|||
import "type_parser.dart"
|
||||
show
|
||||
ParsedClass,
|
||||
ParsedIntersectionType,
|
||||
ParsedFunctionType,
|
||||
ParsedInterfaceType,
|
||||
ParsedType,
|
||||
|
@ -36,7 +38,8 @@ Library parseLibrary(Uri uri, String text,
|
|||
{Uri fileUri, KernelEnvironment environment}) {
|
||||
fileUri ??= uri;
|
||||
environment ??= new KernelEnvironment(uri, fileUri);
|
||||
Library library = new Library(uri, fileUri: fileUri);
|
||||
Library library =
|
||||
new Library(uri, fileUri: fileUri, name: uri.path.replaceAll("/", "."));
|
||||
for (ParsedType type in type_parser.parse(text)) {
|
||||
Node node = environment.kernelFromParsedType(type);
|
||||
if (node is Class) {
|
||||
|
@ -145,14 +148,35 @@ class KernelFromParsedType implements Visitor<Node, KernelEnvironment> {
|
|||
|
||||
FunctionType visitFunctionType(
|
||||
ParsedFunctionType node, KernelEnvironment environment) {
|
||||
DartType returnType =
|
||||
node.returnType?.accept<Node, KernelEnvironment>(this, environment);
|
||||
List<DartType> arguments = <DartType>[];
|
||||
for (ParsedType argument in node.arguments.required) {
|
||||
arguments
|
||||
.add(argument.accept<Node, KernelEnvironment>(this, environment));
|
||||
ParameterEnvironment parameterEnvironment =
|
||||
computeTypeParameterEnvironment(node.typeVariables, environment);
|
||||
List<DartType> positionalParameters = <DartType>[];
|
||||
List<NamedType> namedParameters = <NamedType>[];
|
||||
DartType returnType;
|
||||
{
|
||||
KernelEnvironment environment = parameterEnvironment.environment;
|
||||
returnType =
|
||||
node.returnType?.accept<Node, KernelEnvironment>(this, environment);
|
||||
for (ParsedType argument in node.arguments.required) {
|
||||
positionalParameters
|
||||
.add(argument.accept<Node, KernelEnvironment>(this, environment));
|
||||
}
|
||||
List<Object> optional = node.arguments.optional;
|
||||
for (int i = 0; i < optional.length; i++) {
|
||||
ParsedType parsedType = optional[i];
|
||||
DartType type =
|
||||
parsedType.accept<Node, KernelEnvironment>(this, environment);
|
||||
if (node.arguments.optionalAreNamed) {
|
||||
namedParameters.add(new NamedType(optional[++i], type));
|
||||
} else {
|
||||
positionalParameters.add(type);
|
||||
}
|
||||
}
|
||||
}
|
||||
return new FunctionType(arguments, returnType);
|
||||
return new FunctionType(positionalParameters, returnType,
|
||||
namedParameters: namedParameters,
|
||||
requiredParameterCount: node.arguments.required.length,
|
||||
typeParameters: parameterEnvironment.parameters);
|
||||
}
|
||||
|
||||
VoidType visitVoidType(ParsedVoidType node, KernelEnvironment environment) {
|
||||
|
@ -164,6 +188,14 @@ class KernelFromParsedType implements Visitor<Node, KernelEnvironment> {
|
|||
throw "not implemented: $node";
|
||||
}
|
||||
|
||||
TypeParameterType visitIntersectionType(
|
||||
ParsedIntersectionType node, KernelEnvironment environment) {
|
||||
TypeParameterType type =
|
||||
node.a.accept<Node, KernelEnvironment>(this, environment);
|
||||
DartType bound = node.b.accept<Node, KernelEnvironment>(this, environment);
|
||||
return new TypeParameterType(type.parameter, bound);
|
||||
}
|
||||
|
||||
Supertype toSupertype(InterfaceType type) {
|
||||
return new Supertype.byReference(type.className, type.typeArguments);
|
||||
}
|
||||
|
|
|
@ -14,11 +14,12 @@ import "package:kernel/text/ast_to_text.dart" show Printer;
|
|||
|
||||
import "package:kernel/type_environment.dart" show TypeEnvironment;
|
||||
|
||||
import "kernel_type_parser.dart" show KernelEnvironment, parseLibrary;
|
||||
import "kernel_type_parser.dart"
|
||||
show KernelEnvironment, KernelFromParsedType, parseLibrary;
|
||||
|
||||
import "shared_type_tests.dart" show SubtypeTest;
|
||||
|
||||
import "type_parser.dart" as type_parser show parse;
|
||||
import "type_parser.dart" as type_parser show parse, parseTypeVariables;
|
||||
|
||||
const String testSdk = """
|
||||
class Object;
|
||||
|
@ -35,7 +36,7 @@ const String testSdk = """
|
|||
""";
|
||||
|
||||
const String expectedSdk = """
|
||||
library;
|
||||
library core;
|
||||
import self as self;
|
||||
|
||||
class Object {
|
||||
|
@ -76,7 +77,7 @@ main() {
|
|||
new KernelSubtypeTest(coreTypes, hierarchy, environment).run();
|
||||
}
|
||||
|
||||
class KernelSubtypeTest extends SubtypeTest<DartType> {
|
||||
class KernelSubtypeTest extends SubtypeTest<DartType, KernelEnvironment> {
|
||||
final CoreTypes coreTypes;
|
||||
|
||||
final ClassHierarchy hierarchy;
|
||||
|
@ -85,7 +86,7 @@ class KernelSubtypeTest extends SubtypeTest<DartType> {
|
|||
|
||||
KernelSubtypeTest(this.coreTypes, this.hierarchy, this.environment);
|
||||
|
||||
DartType toType(String text) {
|
||||
DartType toType(String text, KernelEnvironment environment) {
|
||||
return environment.kernelFromParsedType(type_parser.parse(text).single);
|
||||
}
|
||||
|
||||
|
@ -93,4 +94,12 @@ class KernelSubtypeTest extends SubtypeTest<DartType> {
|
|||
return new TypeEnvironment(coreTypes, hierarchy, legacyMode: legacyMode)
|
||||
.isSubtypeOf(subtype, supertype);
|
||||
}
|
||||
|
||||
KernelEnvironment extend(String typeParameters) {
|
||||
if (typeParameters?.isEmpty ?? true) return environment;
|
||||
return const KernelFromParsedType()
|
||||
.computeTypeParameterEnvironment(
|
||||
type_parser.parseTypeVariables("<$typeParameters>"), environment)
|
||||
.environment;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,29 +4,33 @@
|
|||
|
||||
import "package:expect/expect.dart" show Expect;
|
||||
|
||||
abstract class SubtypeTest<T> {
|
||||
abstract class SubtypeTest<T, E> {
|
||||
void isSubtype(String subtypeString, String supertypeString,
|
||||
{bool legacyMode: false, String typeParameters}) {
|
||||
T subtype = toType(subtypeString);
|
||||
T supertype = toType(supertypeString);
|
||||
E environment = extend(typeParameters);
|
||||
T subtype = toType(subtypeString, environment);
|
||||
T supertype = toType(supertypeString, environment);
|
||||
String mode = legacyMode ? " (legacy)" : "";
|
||||
Expect.isTrue(isSubtypeImpl(subtype, supertype, legacyMode),
|
||||
"$subtype should be a subtype of $supertype$mode.");
|
||||
"$subtypeString should be a subtype of $supertypeString$mode.");
|
||||
}
|
||||
|
||||
void isNotSubtype(String subtypeString, String supertypeString,
|
||||
{bool legacyMode: false, String typeParameters}) {
|
||||
T subtype = toType(subtypeString);
|
||||
T supertype = toType(supertypeString);
|
||||
E environment = extend(typeParameters);
|
||||
T subtype = toType(subtypeString, environment);
|
||||
T supertype = toType(supertypeString, environment);
|
||||
String mode = legacyMode ? " (legacy)" : "";
|
||||
Expect.isFalse(isSubtypeImpl(subtype, supertype, legacyMode),
|
||||
"$subtype shouldn't be a subtype of $supertype$mode.");
|
||||
"$subtypeString shouldn't be a subtype of $supertypeString$mode.");
|
||||
}
|
||||
|
||||
T toType(String text);
|
||||
T toType(String text, E environment);
|
||||
|
||||
bool isSubtypeImpl(T subtype, T supertype, bool legacyMode);
|
||||
|
||||
E extend(String typeParameters);
|
||||
|
||||
void run() {
|
||||
isSubtype('int', 'num', legacyMode: true);
|
||||
isSubtype('int', 'Comparable<num>', legacyMode: true);
|
||||
|
@ -68,15 +72,12 @@ abstract class SubtypeTest<T> {
|
|||
isNotSubtype('(num) -> (int) -> int', '(num) -> (num) -> num',
|
||||
legacyMode: true);
|
||||
|
||||
// TODO(ahe): Remove this as the implementation improves.
|
||||
return;
|
||||
|
||||
// ignore: dead_code
|
||||
isSubtype('(x:num) -> num', '(x:int) -> num',
|
||||
isSubtype('({num x}) -> num', '({int x}) -> num',
|
||||
legacyMode: true); // named parameters
|
||||
isSubtype('(num,x:num) -> num', '(int,x:int) -> num', legacyMode: true);
|
||||
isSubtype('(x:num) -> int', '(x:num) -> num', legacyMode: true);
|
||||
isNotSubtype('(x:int) -> int', '(x:num) -> num', legacyMode: true);
|
||||
isSubtype('(num, {num x}) -> num', '(int, {int x}) -> num',
|
||||
legacyMode: true);
|
||||
isSubtype('({num x}) -> int', '({num x}) -> num', legacyMode: true);
|
||||
isNotSubtype('({int x}) -> int', '({num x}) -> num', legacyMode: true);
|
||||
|
||||
isSubtype('<E>(E) -> int', '<E>(E) -> num',
|
||||
legacyMode: true); // type parameters
|
||||
|
@ -94,26 +95,36 @@ abstract class SubtypeTest<T> {
|
|||
isNotSubtype('<E,F>(E) -> (F) -> E', '<F,E>(E) -> (F) -> E',
|
||||
legacyMode: true);
|
||||
|
||||
isNotSubtype('<E>(E,num) -> E', '<E:num>(E,E) -> E', legacyMode: true);
|
||||
isNotSubtype('<E:num>(E) -> int', '<E:int>(E) -> int', legacyMode: true);
|
||||
isNotSubtype('<E:num>(E) -> E', '<E:int>(E) -> E', legacyMode: true);
|
||||
isNotSubtype('<E:num>(int) -> E', '<E:int>(int) -> E', legacyMode: true);
|
||||
isSubtype('<E:num>(E) -> E', '<F:num>(F) -> num', legacyMode: true);
|
||||
isSubtype('<E:int>(E) -> E', '<F:int>(F) -> num', legacyMode: true);
|
||||
isSubtype('<E:int>(E) -> E', '<F:int>(F) -> int', legacyMode: true);
|
||||
isNotSubtype('<E>(E,num) -> E', '<E extends num>(E,E) -> E',
|
||||
legacyMode: true);
|
||||
isNotSubtype('<E extends num>(E) -> int', '<E extends int>(E) -> int',
|
||||
legacyMode: true);
|
||||
isNotSubtype('<E extends num>(E) -> E', '<E extends int>(E) -> E',
|
||||
legacyMode: true);
|
||||
isNotSubtype('<E extends num>(int) -> E', '<E extends int>(int) -> E',
|
||||
legacyMode: true);
|
||||
isSubtype('<E extends num>(E) -> E', '<F extends num>(F) -> num',
|
||||
legacyMode: true);
|
||||
isSubtype('<E extends int>(E) -> E', '<F extends int>(F) -> num',
|
||||
legacyMode: true);
|
||||
isSubtype('<E extends int>(E) -> E', '<F extends int>(F) -> int',
|
||||
legacyMode: true);
|
||||
isNotSubtype('<E>(int) -> int', '(int) -> int', legacyMode: true);
|
||||
isNotSubtype('<E,F>(int) -> int', '<E>(int) -> int', legacyMode: true);
|
||||
|
||||
isSubtype('<E:List<E>>(E) -> E', '<F:List<F>>(F) -> F', legacyMode: true);
|
||||
isNotSubtype('<E:Iterable<E>>(E) -> E', '<F:List<F>>(F) -> F',
|
||||
isSubtype('<E extends List<E>>(E) -> E', '<F extends List<F>>(F) -> F',
|
||||
legacyMode: true);
|
||||
isNotSubtype('<E>(E,List<Object>) -> E', '<F:List<F>>(F,F) -> F',
|
||||
isNotSubtype(
|
||||
'<E extends Iterable<E>>(E) -> E', '<F extends List<F>>(F) -> F',
|
||||
legacyMode: true);
|
||||
isNotSubtype('<E>(E,List<Object>) -> List<E>', '<F:List<F>>(F,F) -> F',
|
||||
isNotSubtype('<E>(E,List<Object>) -> E', '<F extends List<F>>(F,F) -> F',
|
||||
legacyMode: true);
|
||||
isNotSubtype('<E>(E,List<Object>) -> int', '<F:List<F>>(F,F) -> F',
|
||||
isNotSubtype(
|
||||
'<E>(E,List<Object>) -> List<E>', '<F extends List<F>>(F,F) -> F',
|
||||
legacyMode: true);
|
||||
isNotSubtype('<E>(E,List<Object>) -> E', '<F:List<F>>(F,F) -> void',
|
||||
isNotSubtype('<E>(E,List<Object>) -> int', '<F extends List<F>>(F,F) -> F',
|
||||
legacyMode: true);
|
||||
isNotSubtype('<E>(E,List<Object>) -> E', '<F extends List<F>>(F,F) -> void',
|
||||
legacyMode: true);
|
||||
|
||||
isSubtype('int', 'FutureOr<int>');
|
||||
|
@ -130,41 +141,51 @@ abstract class SubtypeTest<T> {
|
|||
isNotSubtype('FutureOr<int>', 'num');
|
||||
|
||||
// T & B <: T & A if B <: A
|
||||
isSubtype('T & int', 'T & int', legacyMode: true);
|
||||
isSubtype('T & int', 'T & num', legacyMode: true);
|
||||
isSubtype('T & num', 'T & num', legacyMode: true);
|
||||
isNotSubtype('T & num', 'T & int', legacyMode: true);
|
||||
isSubtype('T & int', 'T & int', legacyMode: true, typeParameters: 'T');
|
||||
isSubtype('T & int', 'T & num', legacyMode: true, typeParameters: 'T');
|
||||
isSubtype('T & num', 'T & num', legacyMode: true, typeParameters: 'T');
|
||||
isNotSubtype('T & num', 'T & int', legacyMode: true, typeParameters: 'T');
|
||||
|
||||
// T & B <: T extends A if B <: A
|
||||
// (Trivially satisfied since promoted bounds are always a isSubtype of the
|
||||
// original bound)
|
||||
isSubtype('T & int', 'T', legacyMode: true, typeParameters: 'T: int');
|
||||
isSubtype('T & int', 'T', legacyMode: true, typeParameters: 'T: num');
|
||||
isSubtype('T & num', 'T', legacyMode: true, typeParameters: 'T: num');
|
||||
isSubtype('T & int', 'T',
|
||||
legacyMode: true, typeParameters: 'T extends int');
|
||||
isSubtype('T & int', 'T',
|
||||
legacyMode: true, typeParameters: 'T extends num');
|
||||
isSubtype('T & num', 'T',
|
||||
legacyMode: true, typeParameters: 'T extends num');
|
||||
|
||||
// T extends B <: T & A if B <: A
|
||||
isSubtype('T', 'T & int', legacyMode: true, typeParameters: 'T: int');
|
||||
isSubtype('T', 'T & num', legacyMode: true, typeParameters: 'T: int');
|
||||
isSubtype('T', 'T & num', legacyMode: true, typeParameters: 'T: num');
|
||||
isNotSubtype('T', 'T & int', legacyMode: true, typeParameters: 'T: num');
|
||||
isSubtype('T', 'T & int',
|
||||
legacyMode: true, typeParameters: 'T extends int');
|
||||
isSubtype('T', 'T & num',
|
||||
legacyMode: true, typeParameters: 'T extends int');
|
||||
isSubtype('T', 'T & num',
|
||||
legacyMode: true, typeParameters: 'T extends num');
|
||||
isNotSubtype('T', 'T & int',
|
||||
legacyMode: true, typeParameters: 'T extends num');
|
||||
|
||||
// T extends A <: T extends A
|
||||
isSubtype('T', 'T', legacyMode: true, typeParameters: 'T: num');
|
||||
isSubtype('T', 'T', legacyMode: true, typeParameters: 'T extends num');
|
||||
|
||||
// S & B <: A if B <: A, A is not S (or a promotion thereof)
|
||||
isSubtype('S & int', 'int', legacyMode: true);
|
||||
isSubtype('S & int', 'num', legacyMode: true);
|
||||
isSubtype('S & num', 'num', legacyMode: true);
|
||||
isNotSubtype('S & num', 'int', legacyMode: true);
|
||||
isNotSubtype('S & num', 'T', legacyMode: true);
|
||||
isNotSubtype('S & num', 'T & num', legacyMode: true);
|
||||
isSubtype('S & int', 'int', legacyMode: true, typeParameters: 'S');
|
||||
isSubtype('S & int', 'num', legacyMode: true, typeParameters: 'S');
|
||||
isSubtype('S & num', 'num', legacyMode: true, typeParameters: 'S');
|
||||
isNotSubtype('S & num', 'int', legacyMode: true, typeParameters: 'S');
|
||||
isNotSubtype('S & num', 'T', legacyMode: true, typeParameters: 'S, T');
|
||||
isNotSubtype('S & num', 'T & num',
|
||||
legacyMode: true, typeParameters: 'S, T');
|
||||
|
||||
// S extends B <: A if B <: A, A is not S (or a promotion thereof)
|
||||
isSubtype('S', 'int', legacyMode: true, typeParameters: 'S: int');
|
||||
isSubtype('S', 'num', legacyMode: true, typeParameters: 'S: int');
|
||||
isSubtype('S', 'num', legacyMode: true, typeParameters: 'S: num');
|
||||
isNotSubtype('S', 'int', legacyMode: true, typeParameters: 'S: num');
|
||||
isNotSubtype('S', 'T', legacyMode: true, typeParameters: 'S: num');
|
||||
isNotSubtype('S', 'T & num', legacyMode: true, typeParameters: 'S: num');
|
||||
isSubtype('S', 'int', legacyMode: true, typeParameters: 'S extends int');
|
||||
isSubtype('S', 'num', legacyMode: true, typeParameters: 'S extends int');
|
||||
isSubtype('S', 'num', legacyMode: true, typeParameters: 'S extends num');
|
||||
isNotSubtype('S', 'int', legacyMode: true, typeParameters: 'S extends num');
|
||||
isNotSubtype('S', 'T',
|
||||
legacyMode: true, typeParameters: 'S extends num, T');
|
||||
isNotSubtype('S', 'T & num',
|
||||
legacyMode: true, typeParameters: 'S extends num, T');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -109,14 +109,25 @@ class ParsedTypedef extends ParsedDeclaration {
|
|||
}
|
||||
|
||||
class ParsedFunctionType extends ParsedType {
|
||||
final List<ParsedTypeVariable> typeVariables;
|
||||
|
||||
final ParsedType returnType;
|
||||
|
||||
final ParsedArguments arguments;
|
||||
|
||||
ParsedFunctionType(this.returnType, this.arguments);
|
||||
ParsedFunctionType(this.typeVariables, this.returnType, this.arguments);
|
||||
|
||||
String toString() {
|
||||
return "$arguments -> $returnType";
|
||||
StringBuffer sb = new StringBuffer();
|
||||
if (typeVariables.isNotEmpty) {
|
||||
sb.write("<");
|
||||
sb.writeAll(typeVariables, ", ");
|
||||
sb.write(">");
|
||||
}
|
||||
sb.write(arguments);
|
||||
sb.write(" -> ");
|
||||
sb.write(returnType);
|
||||
return "$sb";
|
||||
}
|
||||
|
||||
R accept<R, A>(Visitor<R, A> visitor, [A a]) {
|
||||
|
@ -153,6 +164,26 @@ class ParsedTypeVariable extends ParsedType {
|
|||
}
|
||||
}
|
||||
|
||||
class ParsedIntersectionType extends ParsedType {
|
||||
final ParsedType a;
|
||||
|
||||
final ParsedType b;
|
||||
|
||||
ParsedIntersectionType(this.a, this.b);
|
||||
|
||||
String toString() {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
sb.write(a);
|
||||
sb.write(" & ");
|
||||
sb.write(b);
|
||||
return "$sb";
|
||||
}
|
||||
|
||||
R accept<R, A>(Visitor<R, A> visitor, [A a]) {
|
||||
return visitor.visitIntersectionType(this, a);
|
||||
}
|
||||
}
|
||||
|
||||
class ParsedArguments {
|
||||
final List<ParsedType> required;
|
||||
final List optional;
|
||||
|
@ -203,11 +234,14 @@ class Parser {
|
|||
peek = peek.next;
|
||||
}
|
||||
|
||||
String computeLocation() {
|
||||
return "${source.substring(0, peek.charOffset)}\n>>>"
|
||||
"\n${source.substring(peek.charOffset)}";
|
||||
}
|
||||
|
||||
void expect(String string) {
|
||||
if (!identical(string, peek.stringValue)) {
|
||||
String error = "${source.substring(0, peek.charOffset)}\n>>>"
|
||||
"\n${source.substring(peek.charOffset)}";
|
||||
throw "Expected `$string`, but got `${peek.lexeme}`\n$error";
|
||||
throw "Expected '$string', but got '${peek.lexeme}'\n${computeLocation()}";
|
||||
}
|
||||
advance();
|
||||
}
|
||||
|
@ -228,20 +262,33 @@ class Parser {
|
|||
ParsedType parseType() {
|
||||
if (optional("class")) return parseClass();
|
||||
if (optional("typedef")) return parseTypedef();
|
||||
if (optional("(")) return parseFunctionType();
|
||||
String name = parseName();
|
||||
List<ParsedType> arguments = <ParsedType>[];
|
||||
if (optional("<")) {
|
||||
advance();
|
||||
arguments.add(parseType());
|
||||
while (optional(",")) {
|
||||
advance();
|
||||
arguments.add(parseType());
|
||||
ParsedType result;
|
||||
do {
|
||||
ParsedType type;
|
||||
if (optional("(") || optional("<")) {
|
||||
type = parseFunctionType();
|
||||
} else {
|
||||
String name = parseName();
|
||||
List<ParsedType> arguments = <ParsedType>[];
|
||||
if (optional("<")) {
|
||||
advance();
|
||||
arguments.add(parseType());
|
||||
while (optional(",")) {
|
||||
advance();
|
||||
arguments.add(parseType());
|
||||
}
|
||||
peek = splitCloser(peek) ?? peek;
|
||||
expect(">");
|
||||
}
|
||||
type = new ParsedInterfaceType(name, arguments);
|
||||
}
|
||||
peek = splitCloser(peek);
|
||||
expect(">");
|
||||
}
|
||||
return new ParsedInterfaceType(name, arguments);
|
||||
if (result == null) {
|
||||
result = type;
|
||||
} else {
|
||||
result = new ParsedIntersectionType(result, type);
|
||||
}
|
||||
} while (optionalAdvance("&"));
|
||||
return result;
|
||||
}
|
||||
|
||||
ParsedType parseReturnType() {
|
||||
|
@ -250,16 +297,17 @@ class Parser {
|
|||
}
|
||||
|
||||
ParsedFunctionType parseFunctionType() {
|
||||
List<ParsedTypeVariable> typeVariables = parseTypeVariablesOpt();
|
||||
ParsedArguments arguments = parseArguments();
|
||||
expect("-");
|
||||
expect(">");
|
||||
ParsedType returnType = parseReturnType();
|
||||
return new ParsedFunctionType(returnType, arguments);
|
||||
return new ParsedFunctionType(typeVariables, returnType, arguments);
|
||||
}
|
||||
|
||||
String parseName() {
|
||||
if (!peek.isIdentifier) {
|
||||
throw "Expected a name, but got `${peek.stringValue}`";
|
||||
throw "Expected a name, but got '${peek.stringValue}'\n${computeLocation()}";
|
||||
}
|
||||
String result = peek.lexeme;
|
||||
advance();
|
||||
|
@ -302,7 +350,7 @@ class Parser {
|
|||
do {
|
||||
typeVariables.add(parseTypeVariable());
|
||||
} while (optionalAdvance(","));
|
||||
peek = splitCloser(peek);
|
||||
peek = splitCloser(peek) ?? peek;
|
||||
expect(">");
|
||||
}
|
||||
return typeVariables;
|
||||
|
@ -366,6 +414,16 @@ List<ParsedType> parse(String text) {
|
|||
return types;
|
||||
}
|
||||
|
||||
List<ParsedTypeVariable> parseTypeVariables(String text) {
|
||||
Parser parser = new Parser(scanString(text).tokens, text);
|
||||
List<ParsedType> result = parser.parseTypeVariablesOpt();
|
||||
if (!parser.atEof) {
|
||||
throw "Expected EOF, but got '${parser.peek.stringValue}'\n"
|
||||
"${parser.computeLocation()}";
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
abstract class DefaultAction<R, A> {
|
||||
R defaultAction(ParsedType node, A a);
|
||||
|
||||
|
@ -403,4 +461,8 @@ abstract class Visitor<R, A> {
|
|||
R visitTypeVariable(ParsedTypeVariable node, A a) {
|
||||
return DefaultAction.perform<R, A>(this, node, a);
|
||||
}
|
||||
|
||||
R visitIntersectionType(ParsedIntersectionType node, A a) {
|
||||
return DefaultAction.perform<R, A>(this, node, a);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,5 +37,9 @@ List<List<Object>>
|
|||
List<List<List<Object>>>
|
||||
class A<T extends List<Object>>;
|
||||
class B<T extends List<List<Object>>>;
|
||||
<E>(E) -> int
|
||||
S & T
|
||||
class C;
|
||||
<E>(E) -> int & <E>(E) -> void
|
||||
""");
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue