Add support for the new function-type syntax.

Fixes #27967
BUG= http://dartbug.com/27967
R=ahe@google.com, johnniwinther@google.com, sigmund@google.com

Committed: 85227ba8a6

Reverted: f5fc210f4f

Committed: e4999a6310

Reverted: c64c351539
Review-Url: https://codereview.chromium.org/2567133002 .
This commit is contained in:
Florian Loitsch 2017-02-22 16:53:49 +01:00
parent 3ceb5103cd
commit c289af39c3
45 changed files with 1739 additions and 174 deletions

View file

@ -34,6 +34,7 @@ dart_style:third_party/pkg_tested/dart_style/lib
dartdoc:third_party/pkg/dartdoc/lib
dev_compiler:pkg/dev_compiler/lib
expect:pkg/expect/lib
fasta:pkg/fasta/lib
fixnum:third_party/pkg/fixnum/lib
front_end:pkg/front_end/lib
func:third_party/pkg/func/lib

View file

@ -275,6 +275,7 @@ enum MessageKind {
INVALID_RECEIVER_IN_INITIALIZER,
INVALID_SOURCE_FILE_LOCATION,
INVALID_SYMBOL,
INVALID_INLINE_FUNCTION_TYPE,
INVALID_SYNC_MODIFIER,
INVALID_TYPE_VARIABLE_BOUND,
INVALID_UNNAMED_CONSTRUCTOR_NAME,
@ -3319,6 +3320,16 @@ Please include the following information:
" require a preamble file located in:\n"
" <sdk>/lib/_internal/js_runtime/lib/preambles."),
MessageKind.INVALID_INLINE_FUNCTION_TYPE: const MessageTemplate(
MessageKind.INVALID_INLINE_FUNCTION_TYPE,
"Invalid inline function type.",
howToFix: "Try changing the inline function type (as in 'int f()') to"
" a prefixed function type using the `Function` keyword (as in "
"'int Function() f').",
examples:
const ["typedef F = Function(int f(String x)); main() { F f; }"],
),
MessageKind.INVALID_SYNC_MODIFIER: const MessageTemplate(
MessageKind.INVALID_SYNC_MODIFIER, "Invalid modifier 'sync'.",
howToFix: "Try replacing 'sync' with 'sync*'.",

View file

@ -1208,6 +1208,9 @@ abstract class FormalElement extends Element
FunctionTypedElement get functionDeclaration;
VariableDefinitions get node;
/// Whether the parameter is unnamed in a function type.
bool get isUnnamed;
}
/// A formal parameter of a function or constructor.

View file

@ -1334,7 +1334,7 @@ class TypedefElementX extends ElementX
ResolutionTypedefType computeType(Resolution resolution) {
if (thisTypeCache != null) return thisTypeCache;
Typedef node = parseNode(resolution.parsingContext);
setThisAndRawTypes(createTypeVariables(node.typeParameters));
setThisAndRawTypes(createTypeVariables(node.templateParameters));
ensureResolved(resolution);
return thisTypeCache;
}
@ -1732,6 +1732,15 @@ class FormalElementX extends ElementX
: this.identifier = identifier,
super(identifier.source, elementKind, enclosingElement);
FormalElementX.unnamed(ElementKind elementKind,
FunctionTypedElement enclosingElement,
this.definitions)
: this.identifier = null,
super("<unnamed>", elementKind, enclosingElement);
/// Whether this is an unnamed parameter in a Function type.
bool get isUnnamed => identifier == null;
FunctionTypedElement get functionDeclaration => enclosingElement;
Modifiers get modifiers => definitions.modifiers;

View file

@ -642,6 +642,16 @@ class ResolutionFunctionType extends ResolutionDartType
optionalParameterTypes, namedParameters, namedParameterTypes);
}
factory ResolutionFunctionType.generalized(
ResolutionDartType returnType,
List<ResolutionDartType> parameterTypes,
List<ResolutionDartType> optionalParameterTypes,
List<String> namedParameters,
List<ResolutionDartType> namedParameterTypes) {
return new ResolutionFunctionType.internal(null, returnType, parameterTypes,
optionalParameterTypes, namedParameters, namedParameterTypes);
}
ResolutionFunctionType.internal(FunctionTypedElement this.element,
[ResolutionDartType returnType = const ResolutionDynamicType(),
List<ResolutionDartType> parameterTypes = const <ResolutionDartType>[],

View file

@ -219,7 +219,8 @@ class NoSuchMethodRegistry {
}
if (expr is Send && expr.isTypeCast) {
Send sendExpr = expr;
var typeName = sendExpr.typeAnnotationFromIsCheckOrCast.typeName;
var typeAnnotation = sendExpr.typeAnnotationFromIsCheckOrCast;
var typeName = typeAnnotation.asNominalTypeAnnotation()?.typeName;
if (typeName is Identifier && typeName.source == "dynamic") {
expr = sendExpr.receiver;
}

View file

@ -105,6 +105,7 @@ import '../tree/tree.dart'
ForIn,
FunctionDeclaration,
FunctionExpression,
FunctionTypeAnnotation,
Identifier,
If,
Label,
@ -123,6 +124,7 @@ import '../tree/tree.dart'
NewExpression,
Node,
NodeList,
NominalTypeAnnotation,
Operator,
ParenthesizedExpression,
RedirectingFactoryBody,
@ -1121,14 +1123,28 @@ class KernelVisitor extends Object
@override
visitTypeAnnotation(TypeAnnotation node) {
// Shouldn't be called, as the resolver have already resolved types and
// Shouldn't be called, as the resolver has already resolved types and
// created [DartType] objects.
return internalError(node, "TypeAnnotation");
}
@override
visitNominalTypeAnnotation(NominalTypeAnnotation node) {
// Shouldn't be called, as the resolver has already resolved types and
// created [DartType] objects.
return internalError(node, "NominalTypeAnnotation");
}
@override
visitFunctionTypeAnnotation(FunctionTypeAnnotation node) {
// Shouldn't be called, as the resolver has already resolved types and
// created [DartType] objects.
return internalError(node, "FunctionTypeAnnotation");
}
@override
visitTypeVariable(TypeVariable node) {
// Shouldn't be called, as the resolver have already resolved types and
// Shouldn't be called, as the resolver has already resolved types and
// created [DartType] objects.
return internalError(node, "TypeVariable");
}

View file

@ -262,7 +262,7 @@ class NativeBehavior {
/// Each tag kind (including the 'type-tag's) can only occur once in the
/// sequence.
///
/// [specString] is the specification string, [resolveType] resolves named
/// [specString] is the specification string, [lookupType] resolves named
/// types into type values, [typesReturned] and [typesInstantiated] collects
/// the types defined by the specification string, and [objectType] and
/// [nullType] define the types for `Object` and `Null`, respectively. The

View file

@ -17,7 +17,9 @@ import 'package:front_end/src/fasta/parser.dart'
class PartialParser extends TopLevelParser {
PartialParser(Listener listener) : super(listener);
Token parseFormalParameters(Token token) => skipFormalParameters(token);
Token parseFormalParameters(Token token, {bool inFunctionType: false}) {
return skipFormalParameters(token);
}
}
class DietParserTask extends CompilerTask {

View file

@ -287,10 +287,18 @@ class ElementListener extends Listener {
}
@override
void endFunctionTypeAlias(Token typedefKeyword, Token endToken) {
popNode(); // TODO(karlklose): do not throw away typeVariables.
Identifier name = popNode();
popNode(); // returnType
void endFunctionTypeAlias(
Token typedefKeyword, Token equals, Token endToken) {
Identifier name;
if (equals == null) {
popNode(); // TODO(karlklose): do not throw away typeVariables.
name = popNode();
popNode(); // returnType
} else {
popNode(); // Function type.
popNode(); // TODO(karlklose): do not throw away typeVariables.
name = popNode();
}
pushElement(new PartialTypedefElement(
name.source, compilationUnitElement, typedefKeyword, endToken));
rejectBuiltInIdentifier(name);
@ -324,13 +332,13 @@ class ElementListener extends Listener {
@override
void endMixinApplication() {
NodeList mixins = popNode();
TypeAnnotation superclass = popNode();
NominalTypeAnnotation superclass = popNode();
pushNode(new MixinApplication(superclass, mixins));
}
@override
void handleVoidKeyword(Token token) {
pushNode(new TypeAnnotation(new Identifier(token), null));
pushNode(new NominalTypeAnnotation(new Identifier(token), null));
}
@override
@ -403,7 +411,7 @@ class ElementListener extends Listener {
@override
void endTypeVariable(Token token, Token extendsOrSuper) {
TypeAnnotation bound = popNode();
NominalTypeAnnotation bound = popNode();
Identifier name = popNode();
pushNode(new TypeVariable(name, extendsOrSuper, bound));
rejectBuiltInIdentifier(name);
@ -430,10 +438,21 @@ class ElementListener extends Listener {
}
@override
void endType(Token beginToken, Token endToken) {
void handleType(Token beginToken, Token endToken) {
NodeList typeArguments = popNode();
Expression typeName = popNode();
pushNode(new TypeAnnotation(typeName, typeArguments));
pushNode(new NominalTypeAnnotation(typeName, typeArguments));
}
void handleNoName(Token token) {
pushNode(null);
}
@override
void handleFunctionType(Token functionToken, Token endToken) {
popNode(); // Type parameters.
popNode(); // Return type.
pushNode(null);
}
@override
@ -632,6 +651,10 @@ class ElementListener extends Listener {
errorCode = MessageKind.BAD_INPUT_CHARACTER;
break;
case ErrorKind.InvalidInlineFunctionType:
errorCode = MessageKind.INVALID_INLINE_FUNCTION_TYPE;
break;
case ErrorKind.InvalidSyncModifier:
errorCode = MessageKind.INVALID_SYNC_MODIFIER;
break;

View file

@ -126,13 +126,60 @@ class NodeListener extends ElementListener {
}
@override
void endFunctionTypeAlias(Token typedefKeyword, Token endToken) {
void endFunctionTypeAlias(
Token typedefKeyword, Token equals, Token endToken) {
bool isGeneralizedTypeAlias;
NodeList templateParameters;
TypeAnnotation returnType;
Identifier name;
NodeList typeParameters;
NodeList formals;
if (equals == null) {
isGeneralizedTypeAlias = false;
formals = popNode();
templateParameters = popNode();
name = popNode();
returnType = popNode();
} else {
// TODO(floitsch): keep using the `FunctionTypeAnnotation' node.
isGeneralizedTypeAlias = true;
Node type = popNode();
if (type.asFunctionTypeAnnotation() == null) {
// TODO(floitsch): The parser should diagnose this problem, not
// this listener.
// However, this problem goes away, when we allow aliases for
// non-function types too.
reportFatalError(type, 'Expected a function type.');
}
FunctionTypeAnnotation functionType = type;
templateParameters = popNode();
name = popNode();
returnType = functionType.returnType;
typeParameters = functionType.typeParameters;
formals = functionType.formals;
}
pushNode(new Typedef(
isGeneralizedTypeAlias,
templateParameters,
returnType,
name,
typeParameters,
formals,
typedefKeyword,
endToken));
}
void handleNoName(Token token) {
pushNode(null);
}
@override
void handleFunctionType(Token functionToken, Token endToken) {
NodeList formals = popNode();
NodeList typeParameters = popNode();
Identifier name = popNode();
TypeAnnotation returnType = popNode();
pushNode(new Typedef(
returnType, name, typeParameters, formals, typedefKeyword, endToken));
pushNode(new FunctionTypeAnnotation(
returnType, functionToken, typeParameters, formals));
}
@override
@ -229,7 +276,7 @@ class NodeListener extends ElementListener {
NodeList typeArguments = popNode();
Node classReference = popNode();
if (typeArguments != null) {
classReference = new TypeAnnotation(classReference, typeArguments);
classReference = new NominalTypeAnnotation(classReference, typeArguments);
} else {
Identifier identifier = classReference.asIdentifier();
Send send = classReference.asSend();
@ -867,7 +914,7 @@ class NodeListener extends ElementListener {
NodeList typeArguments = popNode();
Node receiver = popNode();
if (typeArguments != null) {
receiver = new TypeAnnotation(receiver, typeArguments);
receiver = new NominalTypeAnnotation(receiver, typeArguments);
recoverableError(typeArguments, 'Type arguments are not allowed here.');
} else {
Identifier identifier = receiver.asIdentifier();

View file

@ -42,7 +42,9 @@ import 'node_listener.dart' show NodeListener;
class ClassElementParser extends ClassMemberParser {
ClassElementParser(Listener listener) : super(listener);
Token parseFormalParameters(Token token) => skipFormalParameters(token);
Token parseFormalParameters(Token token, {bool inFunctionType: false}) {
return skipFormalParameters(token);
}
}
abstract class PartialElement implements DeclarationSite {

View file

@ -66,8 +66,8 @@ class TypeDefinitionVisitor extends MappingVisitor<ResolutionDartType> {
TypeVariableElementX variableElement = typeVariable.element;
if (typeNode.bound != null) {
ResolutionDartType boundType =
typeResolver.resolveTypeAnnotation(this, typeNode.bound);
ResolutionDartType boundType = typeResolver
.resolveNominalTypeAnnotation(this, typeNode.bound, const []);
variableElement.boundCache = boundType;
void checkTypeVariableBound() {
@ -250,8 +250,8 @@ class ClassResolverVisitor extends TypeDefinitionVisitor {
/// Resolves the mixed type for [mixinNode] and checks that the mixin type
/// is a valid, non-blacklisted interface type. The mixin type is returned.
ResolutionDartType checkMixinType(TypeAnnotation mixinNode) {
ResolutionDartType mixinType = resolveType(mixinNode);
ResolutionDartType checkMixinType(NominalTypeAnnotation mixinNode) {
ResolutionDartType mixinType = resolveNominalType(mixinNode);
if (isBlackListed(mixinType)) {
reporter.reportErrorMessage(
mixinNode, MessageKind.CANNOT_MIXIN, {'type': mixinType});
@ -440,13 +440,13 @@ class ClassResolverVisitor extends TypeDefinitionVisitor {
return mixinType;
}
ResolutionDartType resolveType(TypeAnnotation node) {
return typeResolver.resolveTypeAnnotation(this, node);
ResolutionDartType resolveNominalType(NominalTypeAnnotation node) {
return typeResolver.resolveNominalTypeAnnotation(this, node, const []);
}
ResolutionDartType resolveSupertype(
ClassElement cls, TypeAnnotation superclass) {
ResolutionDartType supertype = resolveType(superclass);
ClassElement cls, NominalTypeAnnotation superclass) {
ResolutionDartType supertype = resolveNominalType(superclass);
if (supertype != null) {
if (supertype.isMalformed) {
reporter.reportErrorMessage(
@ -476,7 +476,7 @@ class ClassResolverVisitor extends TypeDefinitionVisitor {
Link<ResolutionDartType> result = const Link<ResolutionDartType>();
if (interfaces == null) return result;
for (Link<Node> link = interfaces.nodes; !link.isEmpty; link = link.tail) {
ResolutionDartType interfaceType = resolveType(link.head);
ResolutionDartType interfaceType = resolveNominalType(link.head);
if (interfaceType != null) {
if (interfaceType.isMalformed) {
reporter.reportErrorMessage(
@ -490,7 +490,7 @@ class ClassResolverVisitor extends TypeDefinitionVisitor {
{'className': element.name, 'enumType': interfaceType});
} else if (!interfaceType.isInterfaceType) {
// TODO(johnniwinther): Handle dynamic.
TypeAnnotation typeAnnotation = link.head;
NominalTypeAnnotation typeAnnotation = link.head;
reporter.reportErrorMessage(
typeAnnotation.typeName, MessageKind.CLASS_NAME_EXPECTED);
} else {
@ -615,7 +615,7 @@ class ClassSupertypeResolver extends CommonResolverVisitor {
visitNodeList(node.interfaces);
}
void visitTypeAnnotation(TypeAnnotation node) {
void visitNominalTypeAnnotation(NominalTypeAnnotation node) {
node.typeName.accept(this);
}

View file

@ -618,7 +618,7 @@ class ConstructorResolver extends CommonResolverVisitor<ConstructorResult> {
return result;
}
ConstructorResult visitTypeAnnotation(TypeAnnotation node) {
ConstructorResult visitNominalTypeAnnotation(NominalTypeAnnotation node) {
// This is not really resolving a type-annotation, but the name of the
// constructor. Therefore we allow deferred types.
ResolutionDartType type = resolver.resolveTypeAnnotation(node,

View file

@ -56,6 +56,10 @@ abstract class MappingVisitor<T> extends CommonResolverVisitor<T> {
/// Add [element] to the current scope and check for duplicate definitions.
void addToScope(Element element) {
if (element is FormalElement && element.isUnnamed) {
// No duplicate names possible.
return;
}
Element existing = scope.add(element);
if (existing != element) {
reportDuplicateDefinition(element.name, element, existing);

View file

@ -93,7 +93,9 @@ class SignatureResolver extends MappingVisitor<FormalElementX> {
reporter.internalError(node, 'function type parameters not supported');
}
currentDefinitions = node;
FormalElementX element = definition.accept(this);
FormalElementX element = definition == null
? createUnnamedParameter() // This happens in function types.
: definition.accept(this);
if (currentDefinitions.metadata != null) {
element.metadataInternal =
resolution.resolver.resolveMetadata(element, node);
@ -112,7 +114,8 @@ class SignatureResolver extends MappingVisitor<FormalElementX> {
void computeParameterType(FormalElementX element,
[VariableElement fieldElement]) {
void computeFunctionType(FunctionExpression functionExpression) {
// Function-type as in `foo(int bar(String x))`
void computeInlineFunctionType(FunctionExpression functionExpression) {
FunctionSignature functionSignature = SignatureResolver.analyze(
resolution,
scope,
@ -134,13 +137,14 @@ class SignatureResolver extends MappingVisitor<FormalElementX> {
assert(invariant(currentDefinitions, link.tail.isEmpty));
if (link.head.asFunctionExpression() != null) {
// Inline function typed parameter, like `void m(int f(String s))`.
computeFunctionType(link.head);
computeInlineFunctionType(link.head);
} else if (link.head.asSend() != null &&
link.head.asSend().selector.asFunctionExpression() != null) {
// Inline function typed initializing formal or
// parameter with default value, like `C(int this.f(String s))` or
// `void m([int f(String s) = null])`.
computeFunctionType(link.head.asSend().selector.asFunctionExpression());
computeInlineFunctionType(
link.head.asSend().selector.asFunctionExpression());
} else {
assert(invariant(currentDefinitions,
link.head.asIdentifier() != null || link.head.asSend() != null));
@ -197,6 +201,15 @@ class SignatureResolver extends MappingVisitor<FormalElementX> {
return parameter;
}
FormalElementX createUnnamedParameter() {
FormalElementX parameter;
assert(!createRealParameters);
parameter = new FormalElementX.unnamed(
ElementKind.PARAMETER, enclosingElement, currentDefinitions);
computeParameterType(parameter);
return parameter;
}
InitializingFormalElementX createFieldParameter(
Send node, Expression initializer) {
InitializingFormalElementX element;
@ -426,7 +439,7 @@ class SignatureResolver extends MappingVisitor<FormalElementX> {
List<Element> orderedOptionalParameters =
visitor.optionalParameters.toList();
if (visitor.optionalParametersAreNamed) {
// TODO(karlklose); replace when [visitor.optinalParameters] is a [List].
// TODO(karlklose); replace when [visitor.optionalParameters] is a [List].
orderedOptionalParameters.sort((Element a, Element b) {
return a.name.compareTo(b.name);
});
@ -441,7 +454,7 @@ class SignatureResolver extends MappingVisitor<FormalElementX> {
namedParameterTypes =
namedParameterTypesBuilder.toLink().toList(growable: false);
} else {
// TODO(karlklose); replace when [visitor.optinalParameters] is a [List].
// TODO(karlklose); replace when [visitor.optionalParameters] is a [List].
LinkBuilder<ResolutionDartType> optionalParameterTypesBuilder =
new LinkBuilder<ResolutionDartType>();
for (FormalElement parameter in visitor.optionalParameters) {

View file

@ -27,6 +27,16 @@ import 'registry.dart' show ResolutionRegistry;
import 'resolution_common.dart' show MappingVisitor;
import 'scope.dart' show Scope;
class _FormalsTypeResolutionResult {
final List<ResolutionDartType> requiredTypes;
final List<ResolutionDartType> orderedTypes;
final List<String> names;
final List<ResolutionDartType> nameTypes;
_FormalsTypeResolutionResult(
this.requiredTypes, this.orderedTypes, this.names, this.nameTypes);
}
class TypeResolver {
final Resolution resolution;
@ -66,6 +76,154 @@ class TypeResolver {
ResolutionDartType resolveTypeAnnotation(
MappingVisitor visitor, TypeAnnotation node,
{bool malformedIsError: false, bool deferredIsMalformed: true}) {
return _resolveTypeAnnotation(
visitor, node, const [], malformedIsError: malformedIsError,
deferredIsMalformed: deferredIsMalformed);
}
// TODO(floitsch): the [visibleTypeParameterNames] is a hack to put
// type parameters in scope for the nested types.
//
// For example, in the following example, the generic type "A" would be stored
// in `visibleTypeParameterNames`.
// `typedef F = Function(List<A> Function<A>(A x))`.
//
// They are resolved to `dynamic` until dart2js supports generic methods.
ResolutionDartType _resolveTypeAnnotation(
MappingVisitor visitor, TypeAnnotation node,
List<List<String>> visibleTypeParameterNames,
{bool malformedIsError: false, bool deferredIsMalformed: true}) {
if (node.asNominalTypeAnnotation() != null) {
return resolveNominalTypeAnnotation(
visitor, node,
visibleTypeParameterNames,
malformedIsError: malformedIsError,
deferredIsMalformed: deferredIsMalformed);
}
assert(node.asFunctionTypeAnnotation() != null);
return _resolveFunctionTypeAnnotation(
visitor, node, visibleTypeParameterNames,
malformedIsError: malformedIsError,
deferredIsMalformed: deferredIsMalformed);
}
/// Resolves the types of a parameter list.
///
/// This function does not accept "inline" function types. For example
/// `foo(int bar(String x))` is not accepted.
///
/// However, it does work with nested generalized function types:
/// `foo(int Function(String) x)`.
_FormalsTypeResolutionResult _resolveFormalTypes(
MappingVisitor visitor, NodeList formals,
List<List<String>> visibleTypeParameterNames) {
ResolutionDartType resolvePositionalType(VariableDefinitions node) {
return _resolveTypeAnnotation(
visitor, node.type, visibleTypeParameterNames);
}
void fillNamedTypes(NodeList namedFormals,
List<String> names, List<ResolutionDartType> types) {
List<Node> nodes = namedFormals.nodes.toList(growable: false);
// Sort the named arguments first.
nodes.sort((node1, node2) {
VariableDefinitions a = node1;
VariableDefinitions b = node2;
assert(a.definitions.nodes.tail.isEmpty);
assert(b.definitions.nodes.tail.isEmpty);
return a.definitions.nodes.head.asIdentifier().source.compareTo(
b.definitions.nodes.head.asIdentifier().source);
});
for (VariableDefinitions node in nodes) {
String name = node.definitions.nodes.head.asIdentifier().source;
ResolutionDartType type = node.type == null
? const ResolutionDynamicType()
: _resolveTypeAnnotation(
visitor, node.type, visibleTypeParameterNames);
names.add(name);
types.add(type);
}
}
List<ResolutionDartType> requiredTypes = <ResolutionDartType>[];
NodeList optionalFormals = null;
for (Link<Node> link = formals.nodes; !link.isEmpty; link = link.tail) {
if (link.tail.isEmpty && link.head is NodeList) {
optionalFormals = link.head;
break;
}
requiredTypes.add(resolvePositionalType(link.head));
}
List<ResolutionDartType> orderedTypes = const <ResolutionDartType>[];
List<String> names = const <String>[];
List<ResolutionDartType> namedTypes = const <ResolutionDartType>[];
if (optionalFormals != null) {
// This must be a list of optional arguments.
String value = optionalFormals.beginToken.stringValue;
if ((!identical(value, '[')) && (!identical(value, '{'))) {
reporter.internalError(optionalFormals, "expected optional parameters");
}
bool optionalParametersAreNamed = (identical(value, '{'));
if (optionalParametersAreNamed) {
names = <String>[];
namedTypes = <ResolutionDartType>[];
fillNamedTypes(optionalFormals, names, namedTypes);
} else {
orderedTypes = <ResolutionDartType>[];
for (Link<Node> link = optionalFormals.nodes;
!link.isEmpty;
link = link.tail) {
orderedTypes.add(resolvePositionalType(link.head));
}
}
}
return new _FormalsTypeResolutionResult(
requiredTypes, orderedTypes, names, namedTypes);
}
ResolutionFunctionType _resolveFunctionTypeAnnotation(MappingVisitor visitor,
FunctionTypeAnnotation node,
List<List<String>> visibleTypeParameterNames,
{bool malformedIsError: false, bool deferredIsMalformed: true}) {
assert(visibleTypeParameterNames != null);
if (node.typeParameters != null) {
List<String> newTypeNames = node.typeParameters.map((TypeVariable node) {
return node.name.asIdentifier().source;
}).toList();
visibleTypeParameterNames =
visibleTypeParameterNames.toList()..add(newTypeNames);
}
ResolutionDartType returnType = node.returnType == null
? const ResolutionDynamicType()
: _resolveTypeAnnotation(visitor, node.returnType,
visibleTypeParameterNames);
var formalTypes =
_resolveFormalTypes(visitor, node.formals, visibleTypeParameterNames);
var result = new ResolutionFunctionType.generalized(
returnType,
formalTypes.requiredTypes,
formalTypes.orderedTypes,
formalTypes.names,
formalTypes.nameTypes);
visitor.registry.useType(node, result);
return result;
}
ResolutionDartType resolveNominalTypeAnnotation(MappingVisitor visitor,
NominalTypeAnnotation node,
List<List<String>> visibleTypeParameterNames,
{bool malformedIsError: false, bool deferredIsMalformed: true}) {
ResolutionRegistry registry = visitor.registry;
Identifier typeName;
@ -74,7 +232,8 @@ class TypeResolver {
ResolutionDartType checkNoTypeArguments(ResolutionDartType type) {
List<ResolutionDartType> arguments = new List<ResolutionDartType>();
bool hasTypeArgumentMismatch = resolveTypeArguments(
visitor, node, const <ResolutionDartType>[], arguments);
visitor, node, const <ResolutionDartType>[], arguments,
visibleTypeParameterNames);
if (hasTypeArgumentMismatch) {
return new MalformedType(
new ErroneousElementX(MessageKind.TYPE_ARGUMENT_COUNT_MISMATCH,
@ -106,9 +265,6 @@ class TypeResolver {
}
}
Element element = resolveTypeName(prefixName, typeName, visitor.scope,
deferredIsMalformed: deferredIsMalformed);
ResolutionDartType reportFailureAndCreateType(
MessageKind messageKind, Map messageArguments,
{ResolutionDartType userProvidedBadType,
@ -128,14 +284,33 @@ class TypeResolver {
typeName.source, visitor.enclosingElement);
}
List<ResolutionDartType> arguments = <ResolutionDartType>[];
resolveTypeArguments(
visitor, node, const <ResolutionDartType>[], arguments);
resolveTypeArguments(visitor, node, const <ResolutionDartType>[],
arguments, visibleTypeParameterNames);
return new MalformedType(
erroneousElement, userProvidedBadType, arguments);
}
Element element;
// Resolve references to type names as dynamic.
// TODO(floitsch): this hackishly resolves generic function type arguments
// to dynamic.
if (prefixName == null &&
visibleTypeParameterNames.any((n) => n.contains(typeName.source))) {
type = const ResolutionDynamicType();
} else {
element = resolveTypeName(prefixName, typeName, visitor.scope,
deferredIsMalformed: deferredIsMalformed);
}
// Try to construct the type from the element.
if (element == null) {
if (type != null) {
// Already assigned to through the visibleTypeParameterNames.
// Just make sure that it doesn't have type arguments.
if (node.typeArguments != null) {
reporter.reportWarningMessage(node.typeArguments.nodes.head,
MessageKind.ADDITIONAL_TYPE_ARGUMENT);
}
} else if (element == null) {
type = reportFailureAndCreateType(
MessageKind.CANNOT_RESOLVE_TYPE, {'typeName': node.typeName});
} else if (element.isAmbiguous) {
@ -170,8 +345,9 @@ class TypeResolver {
resolver.ensureClassWillBeResolvedInternal(cls);
cls.computeType(resolution);
List<ResolutionDartType> arguments = <ResolutionDartType>[];
bool hasTypeArgumentMismatch =
resolveTypeArguments(visitor, node, cls.typeVariables, arguments);
bool hasTypeArgumentMismatch = resolveTypeArguments(
visitor, node, cls.typeVariables, arguments,
visibleTypeParameterNames);
if (hasTypeArgumentMismatch) {
type = new BadInterfaceType(
cls.declaration,
@ -194,7 +370,8 @@ class TypeResolver {
typdef.computeType(resolution);
List<ResolutionDartType> arguments = <ResolutionDartType>[];
bool hasTypeArgumentMismatch = resolveTypeArguments(
visitor, node, typdef.typeVariables, arguments);
visitor, node, typdef.typeVariables, arguments,
visibleTypeParameterNames);
if (hasTypeArgumentMismatch) {
type = new BadTypedefType(
typdef,
@ -241,7 +418,7 @@ class TypeResolver {
}
/// Checks the type arguments of [type] against the type variable bounds.
void checkTypeVariableBounds(TypeAnnotation node, GenericType type) {
void checkTypeVariableBounds(NominalTypeAnnotation node, GenericType type) {
void checkTypeVariableBound(_, ResolutionDartType typeArgument,
ResolutionTypeVariableType typeVariable, ResolutionDartType bound) {
if (!types.isSubtype(typeArgument, bound)) {
@ -266,9 +443,10 @@ class TypeResolver {
*/
bool resolveTypeArguments(
MappingVisitor visitor,
TypeAnnotation node,
NominalTypeAnnotation node,
List<ResolutionDartType> typeVariables,
List<ResolutionDartType> arguments) {
List<ResolutionDartType> arguments,
List<List<String>> visibleTypeParameterNames) {
if (node.typeArguments == null) {
return false;
}
@ -283,8 +461,8 @@ class TypeResolver {
typeArguments.head, MessageKind.ADDITIONAL_TYPE_ARGUMENT);
typeArgumentCountMismatch = true;
}
ResolutionDartType argType =
resolveTypeAnnotation(visitor, typeArguments.head);
ResolutionDartType argType = _resolveTypeAnnotation(
visitor, typeArguments.head, visibleTypeParameterNames);
// TODO(karlklose): rewrite to not modify [arguments].
arguments.add(argType);
}

View file

@ -27,12 +27,12 @@ class TypedefResolverVisitor extends TypeDefinitionVisitor {
visitTypedef(Typedef node) {
element.computeType(resolution);
scope = new TypeDeclarationScope(scope, element);
resolveTypeVariableBounds(node.typeParameters);
resolveTypeVariableBounds(node.templateParameters);
FunctionSignature signature = SignatureResolver.analyze(
resolution,
scope,
null /* typeVariables */,
node.typeParameters,
node.formals,
node.returnType,
element,

View file

@ -2044,13 +2044,24 @@ class NodeEquivalenceVisitor implements Visitor1<bool, Node> {
}
@override
bool visitTypeAnnotation(TypeAnnotation node1, TypeAnnotation node2) {
bool visitNominalTypeAnnotation(
NominalTypeAnnotation node1, NominalTypeAnnotation node2) {
return testNodes(
node1, node2, 'typeName', node1.typeName, node2.typeName) &&
testNodes(node1, node2, 'typeArguments', node1.typeArguments,
node2.typeArguments);
}
@override
bool visitFunctionTypeAnnotation(
FunctionTypeAnnotation node1, FunctionTypeAnnotation node2) {
return testNodes(
node1, node2, 'returnType', node1.returnType, node2.returnType) &&
testNodes(node1, node2, 'formals', node1.formals, node2.formals) &&
testNodes(node1, node2, 'typeParameters', node1.typeParameters,
node2.typeParameters);
}
@override
bool visitTypeVariable(TypeVariable node1, TypeVariable node2) {
return testNodes(node1, node2, 'name', node1.name, node2.name) &&
@ -2065,8 +2076,8 @@ class NodeEquivalenceVisitor implements Visitor1<bool, Node> {
testNodes(
node1, node2, 'returnType', node1.returnType, node2.returnType) &&
testNodes(node1, node2, 'name', node1.name, node2.name) &&
testNodes(node1, node2, 'typeParameters', node1.typeParameters,
node2.typeParameters) &&
testNodes(node1, node2, 'typeParameters', node1.templateParameters,
node2.templateParameters) &&
testNodes(node1, node2, 'formals', node1.formals, node2.formals);
}
@ -2121,6 +2132,11 @@ class NodeEquivalenceVisitor implements Visitor1<bool, Node> {
bool visitStringNode(StringNode node1, StringNode node2) {
throw new UnsupportedError('Unexpected nodes: $node1 <> $node2');
}
@override
bool visitTypeAnnotation(TypeAnnotation node1, TypeAnnotation node2) {
throw new UnsupportedError('Unexpected nodes: $node1 <> $node2');
}
}
bool areMetadataAnnotationsEquivalent(

View file

@ -2144,6 +2144,9 @@ class LocalParameterElementZ extends ParameterElementZ
@override
ElementKind get kind => ElementKind.PARAMETER;
@override
bool get isUnnamed => false;
}
class InitializingFormalElementZ extends LocalParameterElementZ

View file

@ -261,6 +261,14 @@ class ReifiedTypeRepresentationBuilder
ResolutionTypeVariableType type, GraphBuilder builder) {
ClassElement cls = builder.backend.helpers.RuntimeType;
TypeMask instructionType = new TypeMask.subclass(cls, closedWorld);
// TODO(floitsch): this hack maps type variables of generic function
// typedefs to dynamic. For example: `typedef F = Function<T>(T)`.
if (type is MethodTypeVariableType) {
visitDynamicType(const ResolutionDynamicType(), builder);
return;
}
if (!builder.sourceElement.enclosingElement.isClosure &&
builder.sourceElement.isInstanceMember) {
HInstruction receiver = builder.localsHandler.readThis();

View file

@ -51,6 +51,10 @@ abstract class Visitor<R> {
R visitForIn(ForIn node) => visitLoop(node);
R visitFunctionDeclaration(FunctionDeclaration node) => visitStatement(node);
R visitFunctionExpression(FunctionExpression node) => visitExpression(node);
R visitFunctionTypeAnnotation(FunctionTypeAnnotation node) {
return visitTypeAnnotation(node);
}
R visitGotoStatement(GotoStatement node) => visitStatement(node);
R visitIdentifier(Identifier node) => visitExpression(node);
R visitImport(Import node) => visitLibraryDependency(node);
@ -82,6 +86,10 @@ abstract class Visitor<R> {
R visitNewExpression(NewExpression node) => visitExpression(node);
R visitNodeList(NodeList node) => visitNode(node);
R visitNominalTypeAnnotation(NominalTypeAnnotation node) {
return visitTypeAnnotation(node);
}
R visitOperator(Operator node) => visitIdentifier(node);
R visitParenthesizedExpression(ParenthesizedExpression node) {
return visitExpression(node);
@ -179,6 +187,10 @@ abstract class Visitor1<R, A> {
return visitExpression(node, arg);
}
R visitFunctionTypeAnnotation(FunctionTypeAnnotation node, A arg) {
return visitTypeAnnotation(node, arg);
}
R visitGotoStatement(GotoStatement node, A arg) {
return visitStatement(node, arg);
}
@ -228,6 +240,10 @@ abstract class Visitor1<R, A> {
R visitNewExpression(NewExpression node, A arg) => visitExpression(node, arg);
R visitNodeList(NodeList node, A arg) => visitNode(node, arg);
R visitNominalTypeAnnotation(NominalTypeAnnotation node, A arg) {
visitTypeAnnotation(node, arg);
}
R visitOperator(Operator node, A arg) => visitIdentifier(node, arg);
R visitParenthesizedExpression(ParenthesizedExpression node, A arg) {
return visitExpression(node, arg);
@ -263,8 +279,8 @@ abstract class Visitor1<R, A> {
R visitLiteralSymbol(LiteralSymbol node, A arg) => visitExpression(node, arg);
R visitThrow(Throw node, A arg) => visitExpression(node, arg);
R visitTryStatement(TryStatement node, A arg) => visitStatement(node, arg);
R visitTypeAnnotation(TypeAnnotation node, A arg) => visitNode(node, arg);
R visitTypedef(Typedef node, A arg) => visitNode(node, arg);
R visitTypeAnnotation(TypeAnnotation node, A arg) => visitNode(node, arg);
R visitTypeVariable(TypeVariable node, A arg) => visitNode(node, arg);
R visitVariableDefinitions(VariableDefinitions node, A arg) {
return visitStatement(node, arg);
@ -363,6 +379,7 @@ abstract class Node extends NullTreeElementMixin implements Spannable {
ForIn asForIn() => null;
FunctionDeclaration asFunctionDeclaration() => null;
FunctionExpression asFunctionExpression() => null;
FunctionTypeAnnotation asFunctionTypeAnnotation() => null;
Identifier asIdentifier() => null;
If asIf() => null;
Import asImport() => null;
@ -386,6 +403,7 @@ abstract class Node extends NullTreeElementMixin implements Spannable {
NamedMixinApplication asNamedMixinApplication() => null;
NewExpression asNewExpression() => null;
NodeList asNodeList() => null;
NominalTypeAnnotation asNominalTypeAnnotation() => null;
Operator asOperator() => null;
ParenthesizedExpression asParenthesizedExpression() => null;
Part asPart() => null;
@ -404,7 +422,6 @@ abstract class Node extends NullTreeElementMixin implements Spannable {
SwitchStatement asSwitchStatement() => null;
Throw asThrow() => null;
TryStatement asTryStatement() => null;
TypeAnnotation asTypeAnnotation() => null;
TypeVariable asTypeVariable() => null;
Typedef asTypedef() => null;
VariableDefinitions asVariableDefinitions() => null;
@ -489,7 +506,7 @@ class ClassNode extends Node {
}
class MixinApplication extends Node {
final TypeAnnotation superclass;
final NominalTypeAnnotation superclass;
final NodeList mixins;
MixinApplication(this.superclass, this.mixins);
@ -514,8 +531,6 @@ class MixinApplication extends Node {
Token getEndToken() => mixins.getEndToken();
}
// TODO(kasperl): Let this share some structure with the typedef for function
// type aliases?
class NamedMixinApplication extends Node implements MixinApplication {
final Identifier name;
final NodeList typeParameters;
@ -530,7 +545,7 @@ class NamedMixinApplication extends Node implements MixinApplication {
NamedMixinApplication(this.name, this.typeParameters, this.modifiers,
this.mixinApplication, this.interfaces, this.classKeyword, this.endToken);
TypeAnnotation get superclass => mixinApplication.superclass;
NominalTypeAnnotation get superclass => mixinApplication.superclass;
NodeList get mixins => mixinApplication.mixins;
MixinApplication asMixinApplication() => this;
@ -1723,17 +1738,22 @@ class Rethrow extends Statement {
Token getEndToken() => endToken;
}
class TypeAnnotation extends Node {
abstract class TypeAnnotation extends Node {
}
class NominalTypeAnnotation extends TypeAnnotation {
final Expression typeName;
final NodeList typeArguments;
TypeAnnotation(Expression this.typeName, NodeList this.typeArguments);
NominalTypeAnnotation(this.typeName, this.typeArguments);
TypeAnnotation asTypeAnnotation() => this;
NominalTypeAnnotation asNominalTypeAnnotation() => this;
accept(Visitor visitor) => visitor.visitTypeAnnotation(this);
accept(Visitor visitor) => visitor.visitNominalTypeAnnotation(this);
accept1(Visitor1 visitor, arg) => visitor.visitTypeAnnotation(this, arg);
accept1(Visitor1 visitor, arg) {
return visitor.visitNominalTypeAnnotation(this, arg);
}
visitChildren(Visitor visitor) {
typeName.accept(visitor);
@ -1829,7 +1849,13 @@ class VariableDefinitions extends Statement {
return token;
}
Token getEndToken() => definitions.getEndToken();
Token getEndToken() {
var result = definitions.getEndToken();
if (result != null) return result;
assert(definitions.nodes.length == 1);
assert(definitions.nodes.last == null);
return type.getEndToken();
}
}
abstract class Loop extends Statement {
@ -2857,16 +2883,30 @@ class Combinator extends Node {
}
class Typedef extends Node {
final bool isGeneralizedTypeAlias;
/// Parameters to the template.
///
/// For example, `T` and `S` are template parameters in the following
/// typedef: `typedef F<S, T> = Function(S, T)`, or, in the inlined syntax,
/// `typedef F<S, T>(S x, T y)`.
final NodeList templateParameters;
final TypeAnnotation returnType;
final Identifier name;
/// The generic type parameters to the function type.
///
/// For example `A` and `B` (but not `T`) are type parameters in
/// `typedef F<T> = Function<A, B>(A, B, T)`;
final NodeList typeParameters;
final NodeList formals;
final Token typedefKeyword;
final Token endToken;
Typedef(this.returnType, this.name, this.typeParameters, this.formals,
this.typedefKeyword, this.endToken);
Typedef(this.isGeneralizedTypeAlias, this.templateParameters, this.returnType,
this.name, this.typeParameters, this.formals, this.typedefKeyword,
this.endToken);
Typedef asTypedef() => this;
@ -2875,6 +2915,7 @@ class Typedef extends Node {
accept1(Visitor1 visitor, arg) => visitor.visitTypedef(this, arg);
visitChildren(Visitor visitor) {
if (templateParameters != null) templateParameters.accept(visitor);
if (returnType != null) returnType.accept(visitor);
name.accept(visitor);
if (typeParameters != null) typeParameters.accept(visitor);
@ -2882,6 +2923,7 @@ class Typedef extends Node {
}
visitChildren1(Visitor1 visitor, arg) {
if (templateParameters != null) templateParameters.accept1(visitor, arg);
if (returnType != null) returnType.accept1(visitor, arg);
name.accept1(visitor, arg);
if (typeParameters != null) typeParameters.accept1(visitor, arg);
@ -2893,6 +2935,43 @@ class Typedef extends Node {
Token getEndToken() => endToken;
}
class FunctionTypeAnnotation extends TypeAnnotation {
final TypeAnnotation returnType;
final Token functionToken;
final NodeList typeParameters;
final NodeList formals;
FunctionTypeAnnotation(
this.returnType, this.functionToken, this.typeParameters, this.formals);
FunctionTypeAnnotation asFunctionTypeAnnotation() => this;
accept(Visitor visitor) => visitor.visitFunctionTypeAnnotation(this);
accept1(Visitor1 visitor, arg) {
return visitor.visitFunctionTypeAnnotation(this, arg);
}
visitChildren(Visitor visitor) {
if (returnType != null) returnType.accept(visitor);
if (typeParameters != null) typeParameters.accept(visitor);
formals.accept(visitor);
}
visitChildren1(Visitor1 visitor, arg) {
if (returnType != null) returnType.accept1(visitor, arg);
if (typeParameters != null) typeParameters.accept1(visitor, arg);
formals.accept1(visitor, arg);
}
Token getBeginToken() {
if (returnType != null) return returnType.getBeginToken();
return functionToken;
}
Token getEndToken() => formals.getEndToken();
}
class TryStatement extends Statement {
final Block tryBlock;
final NodeList catchBlocks;
@ -3139,6 +3218,8 @@ class ErrorNode extends Node
get type => null;
// Typedef.
get isGeneralizedTypeAlias => null;
get templateParameters => null;
get typeParameters => null;
get formals => null;
get typedefKeyword => null;

View file

@ -153,6 +153,14 @@ class PrettyPrinter extends Indentation with Tagging<Node> implements Visitor {
closeNode();
}
visitFunctionTypeAnnotation(FunctionTypeAnnotation node) {
openNode(node, "FunctionTypeAnnotation");
visitChildNode(node.returnType, "returnType");
visitChildNode(node.typeParameters, "typeParameters");
visitChildNode(node.formals, "formals");
closeNode();
}
visitIdentifier(Identifier node) {
openAndCloseNode(node, "Identifier", {"token": node.token});
}
@ -244,6 +252,13 @@ class PrettyPrinter extends Indentation with Tagging<Node> implements Visitor {
}
}
visitNominalTypeAnnotation(NominalTypeAnnotation node) {
openNode(node, "NominalTypeAnnotation");
visitChildNode(node.typeName, "typeName");
visitChildNode(node.typeArguments, "typeArguments");
closeNode();
}
visitOperator(Operator node) {
openAndCloseNode(node, "Operator", {"value": node.token});
}
@ -345,13 +360,6 @@ class PrettyPrinter extends Indentation with Tagging<Node> implements Visitor {
visitNodeWithChildren(node, "TryStatement");
}
visitTypeAnnotation(TypeAnnotation node) {
openNode(node, "TypeAnnotation");
visitChildNode(node.typeName, "typeName");
visitChildNode(node.typeArguments, "typeArguments");
closeNode();
}
visitTypedef(Typedef node) {
visitNodeWithChildren(node, "Typedef");
}
@ -472,6 +480,10 @@ class PrettyPrinter extends Indentation with Tagging<Node> implements Visitor {
unimplemented('visitNode', node: node);
}
visitTypeAnnotation(TypeAnnotation node) {
unimplemented('visitNode', node: node);
}
unimplemented(String message, {Node node}) {
throw message;
}

View file

@ -548,11 +548,18 @@ class Unparser extends Indentation implements Visitor {
visit(node.expression);
}
visitTypeAnnotation(TypeAnnotation node) {
visitNominalTypeAnnotation(NominalTypeAnnotation node) {
visit(node.typeName);
visit(node.typeArguments);
}
visitFunctionTypeAnnotation(FunctionTypeAnnotation node) {
visit(node.returnType);
write(' Function');
visit(node.typeParameters);
visit(node.formals);
}
visitTypeVariable(TypeVariable node) {
visit(node.name);
if (node.bound != null) {
@ -771,8 +778,8 @@ class Unparser extends Indentation implements Visitor {
write(' ');
}
visit(node.name);
if (node.typeParameters != null) {
visit(node.typeParameters);
if (node.templateParameters != null) {
visit(node.templateParameters);
}
visit(node.formals);
write(node.endToken.value);
@ -904,4 +911,8 @@ class Unparser extends Indentation implements Visitor {
visitStringNode(StringNode node) {
throw 'internal error'; // Should not be called.
}
visitTypeAnnotation(TypeAnnotation node) {
throw 'internal error'; // Should not be called.
}
}

View file

@ -155,7 +155,7 @@ void useNode(tree.Node node) {
..asSwitchStatement()
..asSyncForIn()
..asTryStatement()
..asTypeAnnotation()
..asNominalTypeAnnotation()
..asTypeVariable()
..asTypedef()
..asWhile()

View file

@ -439,7 +439,7 @@ class AstBuilder extends ScopeListener {
push(ast.symbolLiteral(toAnalyzerToken(hashToken), components));
}
void endType(Token beginToken, Token endToken) {
void handleType(Token beginToken, Token endToken) {
debugEvent("Type");
TypeArgumentList arguments = pop();
Identifier name = pop();

View file

@ -1170,7 +1170,7 @@ class BodyBuilder extends ScopeListener<JumpTarget> implements BuilderHelper {
}
@override
void endType(Token beginToken, Token endToken) {
void handleType(Token beginToken, Token endToken) {
// TODO(ahe): The scope is wrong for return types of generic functions.
debugEvent("Type");
List<DartType> arguments = pop();

View file

@ -30,6 +30,7 @@ enum ErrorKind {
ExpectedType,
ExtraneousModifier,
ExtraneousModifierReplace,
InvalidInlineFunctionType,
InvalidAwaitFor,
InvalidSyncModifier,
InvalidVoid,

View file

@ -233,13 +233,22 @@ class Listener {
void beginFunctionTypeAlias(Token token) {}
/// Handle the end of a typedef declaration. Substructures:
/// Handle the end of a typedef declaration.
///
/// If [equals] is null, then we have the following substructures:
/// - Metadata
/// - Return type
/// - Name (identifier)
/// - Type variables
/// - Template variables (type variables to the template)
/// - Formal parameters
void endFunctionTypeAlias(Token typedefKeyword, Token endToken) {
///
/// If [equals] is not null, then the have the following substructures:
/// - Metadata
/// - Name (identifier)
/// - Template variables (type variables to the template)
/// - Type (FunctionTypeAnnotation)
void endFunctionTypeAlias(
Token typedefKeyword, Token equals, Token endToken) {
logEvent("FunctionTypeAlias");
}
@ -263,7 +272,7 @@ class Listener {
/// - mixin application
/// - implemented types (TypeList)
///
/// TODO(paulberry,ahe): it seems incosistent that for a named mixin
/// TODO(paulberry,ahe): it seems inconsistent that for a named mixin
/// application, the implemented types are a TypeList, whereas for a class
/// declaration, each implemented type is listed separately on the stack, and
/// the number of implemented types is passed as a parameter.
@ -634,10 +643,18 @@ class Listener {
logEvent("TryStatement");
}
void endType(Token beginToken, Token endToken) {
void handleType(Token beginToken, Token endToken) {
logEvent("Type");
}
void handleNoName(Token token) {
logEvent("NoName");
}
void handleFunctionType(Token functionToken, Token endToken) {
logEvent("FunctionType");
}
void beginTypeArguments(Token token) {}
void endTypeArguments(int count, Token beginToken, Token endToken) {

View file

@ -100,7 +100,7 @@ class FormalParameterType {
* Subclasses of the class [Listener] are used to listen to events.
*
* Most methods of this class belong in one of two major categories:
* parse metods and peek methods. Parse methods all have the prefix
* parse methods and peek methods. Parse methods all have the prefix
* parse, and peek methods all have the prefix peek.
*
* Parse methods generate events (by calling methods on [listener])
@ -385,11 +385,20 @@ class Parser {
Token parseTypedef(Token token) {
Token typedefKeyword = token;
listener.beginFunctionTypeAlias(token);
token = parseReturnTypeOpt(token.next);
token = parseIdentifier(token);
token = parseTypeVariablesOpt(token);
token = parseFormalParameters(token);
listener.endFunctionTypeAlias(typedefKeyword, token);
Token equals;
if (optional('=', peekAfterNominalType(token.next))) {
token = parseIdentifier(token.next);
token = parseTypeVariablesOpt(token);
equals = token;
token = expect('=', token);
token = parseType(token);
} else {
token = parseReturnTypeOpt(token.next);
token = parseIdentifier(token);
token = parseTypeVariablesOpt(token);
token = parseFormalParameters(token);
}
listener.endFunctionTypeAlias(typedefKeyword, equals, token);
return expect(';', token);
}
@ -404,8 +413,12 @@ class Parser {
Token parseReturnTypeOpt(Token token) {
if (identical(token.stringValue, 'void')) {
listener.handleVoidKeyword(token);
return token.next;
if (isGeneralizedFunctionType(token.next)) {
return parseType(token);
} else {
listener.handleVoidKeyword(token);
return token.next;
}
} else {
return parseTypeOpt(token);
}
@ -437,7 +450,11 @@ class Parser {
return endToken.next;
}
Token parseFormalParameters(Token token) {
/// Parses the formal parameter list of a function.
///
/// If [inFunctionType] is true, then the names may be omitted (except for
/// named arguments). If it is false, then the types may be omitted.
Token parseFormalParameters(Token token, {bool inFunctionType: false}) {
Token begin = token;
listener.beginFormalParameters(begin);
expect('(', token);
@ -450,19 +467,23 @@ class Parser {
++parameterCount;
String value = token.stringValue;
if (identical(value, '[')) {
token = parseOptionalFormalParameters(token, false);
token = parseOptionalFormalParameters(
token, false, inFunctionType: inFunctionType);
break;
} else if (identical(value, '{')) {
token = parseOptionalFormalParameters(token, true);
token = parseOptionalFormalParameters(
token, true, inFunctionType: inFunctionType);
break;
}
token = parseFormalParameter(token, FormalParameterType.REQUIRED);
token = parseFormalParameter(token, FormalParameterType.REQUIRED,
inFunctionType: inFunctionType);
} while (optional(',', token));
listener.endFormalParameters(parameterCount, begin, token);
return expect(')', token);
}
Token parseFormalParameter(Token token, FormalParameterType type) {
Token parseFormalParameter(Token token, FormalParameterType type,
{bool inFunctionType}) {
token = parseMetadataStar(token, forParameter: true);
listener.beginFormalParameter(token);
@ -475,26 +496,51 @@ class Parser {
token = token.next;
}
token = parseModifiers(token);
// TODO(ahe): Validate that there are formal parameters if void.
token = parseReturnTypeOpt(token);
bool isNamedParameter = type == FormalParameterType.NAMED;
Token thisKeyword = null;
if (optional('this', token)) {
thisKeyword = token;
// TODO(ahe): Validate field initializers are only used in
// constructors, and not for function-typed arguments.
token = expect('.', token.next);
if (inFunctionType && isNamedParameter) {
token = parseType(token);
token = parseIdentifier(token);
} else if (inFunctionType) {
token = parseType(token);
if (token.isIdentifier()) {
token = parseIdentifier(token);
} else {
listener.handleNoName(token);
}
} else {
token = parseReturnTypeOpt(token);
if (optional('this', token)) {
thisKeyword = token;
token = expect('.', token.next);
}
token = parseIdentifier(token);
}
token = parseIdentifier(token);
// Generalized function types don't allow inline function types.
// The following isn't allowed:
// int Function(int bar(String x)).
if (optional('(', token)) {
Token inlineFunctionTypeStart = token;
listener.beginFunctionTypedFormalParameter(token);
listener.handleNoTypeVariables(token);
token = parseFormalParameters(token);
listener.endFunctionTypedFormalParameter(token);
if (inFunctionType) {
reportRecoverableError(
inlineFunctionTypeStart, ErrorKind.InvalidInlineFunctionType);
}
} else if (optional('<', token)) {
Token inlineFunctionTypeStart = token;
listener.beginFunctionTypedFormalParameter(token);
token = parseTypeVariablesOpt(token);
token = parseFormalParameters(token);
listener.endFunctionTypedFormalParameter(token);
if (inFunctionType) {
reportRecoverableError(
inlineFunctionTypeStart, ErrorKind.InvalidInlineFunctionType);
}
}
String value = token.stringValue;
if ((identical('=', value)) || (identical(':', value))) {
@ -514,7 +560,8 @@ class Parser {
return token;
}
Token parseOptionalFormalParameters(Token token, bool isNamed) {
Token parseOptionalFormalParameters(Token token, bool isNamed,
{bool inFunctionType}) {
Token begin = token;
listener.beginOptionalFormalParameters(begin);
assert((isNamed && optional('{', token)) || optional('[', token));
@ -528,7 +575,8 @@ class Parser {
}
var type =
isNamed ? FormalParameterType.NAMED : FormalParameterType.POSITIONAL;
token = parseFormalParameter(token, type);
token =
parseFormalParameter(token, type, inFunctionType: inFunctionType);
++parameterCount;
} while (optional(',', token));
if (parameterCount == 0) {
@ -547,6 +595,10 @@ class Parser {
}
Token parseTypeOpt(Token token) {
if (isGeneralizedFunctionType(token)) {
// Function type without return type.
return parseType(token);
}
Token peek = peekAfterIfType(token);
if (peek != null && (peek.isIdentifier() || optional('this', peek))) {
return parseType(token);
@ -721,7 +773,7 @@ class Parser {
modifierCount++;
}
listener.handleModifiers(modifierCount);
bool isMixinApplication = optional('=', peekAfterType(token));
bool isMixinApplication = optional('=', peekAfterNominalType(token));
Token name = token.next;
token = parseIdentifier(name);
@ -760,7 +812,7 @@ class Parser {
Token extendsKeyword;
if (optional('extends', token)) {
extendsKeyword = token;
if (optional('with', peekAfterType(token.next))) {
if (optional('with', peekAfterNominalType(token.next))) {
token = parseMixinApplication(token.next);
} else {
token = parseType(token.next);
@ -852,17 +904,52 @@ class Parser {
!identical(value, token.stringValue);
}
bool isGeneralizedFunctionType(Token token) {
return optional('Function', token) &&
(optional('<', token.next) || optional('(', token.next));
}
Token parseType(Token token) {
Token begin = token;
if (isValidTypeReference(token)) {
token = parseIdentifier(token);
token = parseQualifiedRestOpt(token);
if (isGeneralizedFunctionType(token)) {
// A function type without return type.
// Push the non-existing return type first. The loop below will
// generate the full type.
listener.handleNoType(token);
} else if (identical(token.stringValue, 'void') &&
isGeneralizedFunctionType(token.next)) {
listener.handleVoidKeyword(token);
token = token.next;
} else {
token = reportUnrecoverableError(token, ErrorKind.ExpectedType);
listener.handleInvalidTypeReference(token);
if (isValidTypeReference(token)) {
token = parseIdentifier(token);
token = parseQualifiedRestOpt(token);
} else {
token = reportUnrecoverableError(token, ErrorKind.ExpectedType);
listener.handleInvalidTypeReference(token);
}
token = parseTypeArgumentsOpt(token);
listener.handleType(begin, token);
}
token = parseTypeArgumentsOpt(token);
listener.endType(begin, token);
// While we see a `Function(` treat the pushed type as return type.
// For example: `int Function() Function(int) Function(String x)`.
while (isGeneralizedFunctionType(token)) {
token = parseFunctionType(token);
}
return token;
}
/// Parses a generalized function type.
///
/// The return type must already be pushed.
Token parseFunctionType(Token token) {
assert(optional('Function', token));
Token functionToken = token;
token = token.next;
token = parseTypeVariablesOpt(token);
token = parseFormalParameters(token, inFunctionType: true);
listener.handleFunctionType(functionToken, token);
return token;
}
@ -1082,7 +1169,8 @@ class Parser {
if (!hasType) {
listener.handleNoType(name);
} else if (optional('void', type)) {
} else if (optional('void', type) &&
!isGeneralizedFunctionType(type.next)) {
listener.handleNoType(name);
// TODO(ahe): This error is reported twice, second time is from
// [parseVariablesDeclarationMaybeSemicolon] via
@ -1218,27 +1306,58 @@ class Parser {
hasName = true;
}
identifiers = identifiers.prepend(token);
if (isValidTypeReference(token)) {
// type ...
if (optional('.', token.next)) {
// type '.' ...
if (token.next.next.isIdentifier()) {
// type '.' identifier
token = token.next.next;
if (!isGeneralizedFunctionType(token)) {
// Read a potential return type.
if (isValidTypeReference(token)) {
// type ...
if (optional('.', token.next)) {
// type '.' ...
if (token.next.next.isIdentifier()) {
// type '.' identifier
token = token.next.next;
}
}
}
if (optional('<', token.next)) {
if (token.next is BeginGroupToken) {
BeginGroupToken beginGroup = token.next;
if (beginGroup.endGroup == null) {
reportUnrecoverableError(beginGroup, ErrorKind.UnmatchedToken);
} else {
token = beginGroup.endGroup;
if (optional('<', token.next)) {
if (token.next is BeginGroupToken) {
BeginGroupToken beginGroup = token.next;
if (beginGroup.endGroup == null) {
reportUnrecoverableError(beginGroup, ErrorKind.UnmatchedToken);
} else {
token = beginGroup.endGroup;
}
}
}
}
token = token.next;
}
while (isGeneralizedFunctionType(token)) {
token = token.next;
if (optional('<', token)) {
if (token is BeginGroupToken) {
BeginGroupToken beginGroup = token;
if (beginGroup.endGroup == null) {
reportUnrecoverableError(beginGroup, ErrorKind.UnmatchedToken);
} else {
token = beginGroup.endGroup.next;
}
}
}
if (!optional('(', token)) {
if (optional(';', token)) {
reportRecoverableError(token, ErrorKind.ExpectedOpenParens);
}
token = expect("(", token);
}
if (token is BeginGroupToken) {
BeginGroupToken beginGroup = token;
if (beginGroup.endGroup == null) {
reportUnrecoverableError(beginGroup, ErrorKind.UnmatchedToken);
} else {
token = beginGroup.endGroup.next;
}
}
}
token = token.next;
}
return listener.handleMemberName(const Link<Token>());
}
@ -1353,11 +1472,31 @@ class Parser {
/**
* Returns the first token after the type starting at [token].
*
* This method assumes that [token] is an identifier (or void).
* Use [peekAfterIfType] if [token] isn't known to be an identifier.
*/
Token peekAfterType(Token token) {
// We are looking at "identifier ...".
Token peek = token;
if (!isGeneralizedFunctionType(token)) {
peek = peekAfterNominalType(token);
}
// We might have just skipped over the return value of the function type.
// Check again, if we are now at a function type position.
while (isGeneralizedFunctionType(peek)) {
peek = peekAfterFunctionType(peek.next);
}
return peek;
}
/**
* Returns the first token after the nominal type starting at [token].
*
* This method assumes that [token] is an identifier (or void).
*/
Token peekAfterNominalType(Token token) {
Token peek = token.next;
if (identical(peek.kind, PERIOD_TOKEN)) {
if (peek.next.isIdentifier()) {
@ -1373,12 +1512,57 @@ class Parser {
Token gtToken = beginGroupToken.endGroup;
if (gtToken != null) {
// We are looking at "qualified '<' ... '>' ...".
return gtToken.next;
peek = gtToken.next;
}
}
return peek;
}
/**
* Returns the first token after the function type starting at [token].
*
* The token must be at the token *after* the `Function` token position. That
* is, the return type and the `Function` token must have already been
* skipped.
*
* This function only skips over one function type syntax.
* If necessary, this function must be called multiple times.
*
* Example:
* ```
* int Function() Function<T>(int)
* ^ ^
* A call to this function must be either at `(` or at `<`.
* If `token` pointed to the first `(`, then the returned
* token points to the second `Function` token.
*/
Token peekAfterFunctionType(Token token) {
// Possible inputs are:
// ( ... )
// < ... >( ... )
Token peek = token;
// If there is a generic argument to the function, skip over that one first.
if (identical(peek.kind, LT_TOKEN)) {
BeginGroupToken beginGroupToken = peek;
Token closeToken = beginGroupToken.endGroup;
if (closeToken != null) {
peek = closeToken.next;
}
}
// Now we just need to skip over the formals.
expect('(', peek);
BeginGroupToken beginGroupToken = peek;
Token closeToken = beginGroupToken.endGroup;
if (closeToken != null) {
peek = closeToken.next;
}
return peek;
}
/**
* If [token] is the start of a type, returns the token after that type.
* If [token] is not the start of a type, null is returned.

View file

@ -241,7 +241,7 @@ abstract class AbstractScanner implements Scanner {
if (($A <= next && next <= $Z) ||
identical(next, $_) ||
identical(next, $$)) {
return tokenizeIdentifier(next, scanOffset, true);
return tokenizeKeywordOrIdentifier(next, true);
}
if (identical(next, $LT)) {
@ -808,6 +808,16 @@ abstract class AbstractScanner implements Scanner {
int tokenizeKeywordOrIdentifier(int next, bool allowDollar) {
KeywordState state = KeywordState.KEYWORD_STATE;
int start = scanOffset;
// We allow a leading capital character.
if ($A <= next && next <= $Z) {
state = state.nextCapital(next);
next = advance();
} else if ($a <= next && next <= $z){
// Do the first next call outside the loop to avoid an additional test
// and to make the loop monomorphic.
state = state.next(next);
next = advance();
}
while (state != null && $a <= next && next <= $z) {
state = state.next(next);
next = advance();
@ -945,12 +955,11 @@ abstract class AbstractScanner implements Scanner {
int tokenizeInterpolatedIdentifier(int next) {
appendPrecedenceToken(STRING_INTERPOLATION_IDENTIFIER_INFO);
if ($a <= next && next <= $z) {
if ($a <= next && next <= $z ||
$A <= next && next <= $Z ||
identical(next, $_)) {
beginToken(); // The identifier starts here.
next = tokenizeKeywordOrIdentifier(next, false);
} else if (($A <= next && next <= $Z) || identical(next, $_)) {
beginToken(); // The identifier starts here.
next = tokenizeIdentifier(next, scanOffset, false);
} else {
unterminated(r'$', shouldAdvance: false);
}

View file

@ -5,7 +5,7 @@
library fasta.scanner.keywords;
import 'characters.dart' show
$a;
$a, $z, $A, $Z;
import 'precedence.dart' show
PrecedenceInfo;
@ -77,6 +77,7 @@ class Keyword {
const Keyword("async", isPseudo: true),
const Keyword("await", isPseudo: true),
const Keyword("deferred", isPseudo: true),
const Keyword("Function", isPseudo: true),
const Keyword("hide", isPseudo: true),
const Keyword("native", isPseudo: true),
const Keyword("of", isPseudo: true),
@ -121,10 +122,10 @@ class Keyword {
* Abstract state in a state machine for scanning keywords.
*/
abstract class KeywordState {
KeywordState(this.keyword);
KeywordState next(int c);
final Keyword keyword;
KeywordState nextCapital(int c);
Keyword get keyword;
static KeywordState _KEYWORD_STATE;
static KeywordState get KEYWORD_STATE {
@ -141,7 +142,9 @@ abstract class KeywordState {
static KeywordState computeKeywordStateTable(
int start, List<String> strings, int offset, int length) {
List<KeywordState> result = new List<KeywordState>(26);
bool isLowercase = true;
List<KeywordState> table = new List<KeywordState>($z - $A + 1);
assert(length != 0);
int chunk = 0;
int chunkStart = -1;
@ -152,10 +155,13 @@ abstract class KeywordState {
}
if (strings[i].length > start) {
int c = strings[i].codeUnitAt(start);
if ($A <= c && c <= $Z) {
isLowercase = false;
}
if (chunk != c) {
if (chunkStart != -1) {
assert(result[chunk - $a] == null);
result[chunk - $a] = computeKeywordStateTable(
assert(table[chunk - $A] == null);
table[chunk - $A] = computeKeywordStateTable(
start + 1, strings, chunkStart, i - chunkStart);
}
chunkStart = i;
@ -164,17 +170,19 @@ abstract class KeywordState {
}
}
if (chunkStart != -1) {
assert(result[chunk - $a] == null);
result[chunk - $a] = computeKeywordStateTable(
assert(table[chunk - $A] == null);
table[chunk - $A] = computeKeywordStateTable(
start + 1, strings, chunkStart, offset + length - chunkStart);
} else {
assert(length == 1);
return new LeafKeywordState(strings[offset]);
}
if (isLeaf) {
return new ArrayKeywordState(result, strings[offset]);
String syntax = isLeaf ? strings[offset] : null;
if (isLowercase) {
table = table.sublist($a - $A);
return new LowerCaseArrayKeywordState(table, syntax);
} else {
return new ArrayKeywordState(result, null);
return new UpperCaseArrayKeywordState(table, syntax);
}
}
}
@ -182,13 +190,16 @@ abstract class KeywordState {
/**
* A state with multiple outgoing transitions.
*/
class ArrayKeywordState extends KeywordState {
abstract class ArrayKeywordState implements KeywordState {
final List<KeywordState> table;
final Keyword keyword;
ArrayKeywordState(List<KeywordState> this.table, String syntax)
: super((syntax == null) ? null : Keyword.keywords[syntax]);
: keyword = ((syntax == null) ? null : Keyword.keywords[syntax]);
KeywordState next(int c) => table[c - $a];
KeywordState next(int c);
KeywordState nextCapital(int c);
String toString() {
StringBuffer sb = new StringBuffer();
@ -210,13 +221,42 @@ class ArrayKeywordState extends KeywordState {
}
}
class LowerCaseArrayKeywordState extends ArrayKeywordState {
LowerCaseArrayKeywordState(List<KeywordState> table, String syntax)
: super(table, syntax) {
assert(table.length == $z - $a + 1);
}
KeywordState next(int c) => table[c - $a];
KeywordState nextCapital(int c) => null;
}
class UpperCaseArrayKeywordState extends ArrayKeywordState {
UpperCaseArrayKeywordState(List<KeywordState> table, String syntax)
: super(table, syntax) {
assert(table.length == $z - $A + 1);
}
KeywordState next(int c) => table[c - $A];
KeywordState nextCapital(int c) => table[c - $A];
}
/**
* A state that has no outgoing transitions.
*/
class LeafKeywordState extends KeywordState {
LeafKeywordState(String syntax) : super(Keyword.keywords[syntax]);
class LeafKeywordState implements KeywordState {
final Keyword keyword;
LeafKeywordState(String syntax) : keyword = Keyword.keywords[syntax];
KeywordState next(int c) => null;
KeywordState nextCapital(int c) => null;
String toString() => keyword.syntax;
}

View file

@ -114,7 +114,7 @@ abstract class Token {
}
/**
* A [SymbolToken] represents the symbol in its precendence info.
* A [SymbolToken] represents the symbol in its precedence info.
* Also used for end of file with EOF_INFO.
*/
class SymbolToken extends Token {

View file

@ -133,7 +133,7 @@ class DietListener extends StackListener {
}
@override
void endType(Token beginToken, Token endToken) {
void handleType(Token beginToken, Token endToken) {
debugEvent("Type");
discard(1);
}
@ -190,7 +190,8 @@ class DietListener extends StackListener {
}
@override
void endFunctionTypeAlias(Token typedefKeyword, Token endToken) {
void endFunctionTypeAlias(
Token typedefKeyword, Token equals, Token endToken) {
debugEvent("FunctionTypeAlias");
discard(2); // Name + endToken.
checkEmpty(typedefKeyword.charOffset);

View file

@ -25,7 +25,9 @@ class DietParser extends ClassMemberParser {
DietParser(Listener listener, {bool asyncAwaitKeywordsEnabled: false})
: super(listener, asyncAwaitKeywordsEnabled: asyncAwaitKeywordsEnabled);
Token parseFormalParameters(Token token) => skipFormals(token);
Token parseFormalParameters(Token token, {bool inFunctionType: false}) {
return skipFormals(token);
}
Token skipFormals(Token token) {
listener.beginOptionalFormalParameters(token);

View file

@ -346,7 +346,7 @@ class OutlineBuilder extends UnhandledListener {
}
@override
void endType(Token beginToken, Token endToken) {
void handleType(Token beginToken, Token endToken) {
debugEvent("Type");
List<TypeBuilder> arguments = pop();
String name = pop();
@ -459,7 +459,8 @@ class OutlineBuilder extends UnhandledListener {
}
@override
void endFunctionTypeAlias(Token typedefKeyword, Token endToken) {
void endFunctionTypeAlias(
Token typedefKeyword, Token equals, Token endToken) {
debugEvent("endFunctionTypeAlias");
List<FormalParameterBuilder> formals = pop();
List<TypeVariableBuilder> typeVariables = pop();

View file

@ -0,0 +1,13 @@
// Copyright (c) 2017, 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.
// Tests that an inline function type inside a `Function` type isn't a
// parser error.
typedef F = int Function(int f(String x));
main() {
F f = null;
String Function(String g(int y)) g = null;
}

View file

@ -149,8 +149,9 @@ Future testTypeVariables() {
visitor.visit(definition.type);
ResolutionInterfaceType type =
visitor.registry.mapping.getType(definition.type);
NominalTypeAnnotation annotation = definition.type;
Expect.equals(
definition.type.typeArguments.slowLength(), type.typeArguments.length);
annotation.typeArguments.slowLength(), type.typeArguments.length);
int index = 0;
for (ResolutionDartType argument in type.typeArguments) {
Expect.equals(true, index < expectedElements.length);
@ -700,7 +701,8 @@ Future testTopLevelFields() {
Expect.equals(ElementKind.FIELD, element.kind);
VariableDefinitions node =
element.variables.parseNode(element, compiler.parsingContext);
Identifier typeName = node.type.typeName;
NominalTypeAnnotation annotation = node.type;
Identifier typeName = annotation.typeName;
Expect.equals(typeName.source, 'int');
compiler.parseScript("var b, c;");

View file

@ -0,0 +1,770 @@
// Copyright (c) 2016, 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.
import 'dart:io';
// By convention:
//
// T: generic type of typedef.
// A: generic type of returned function.
// B: generic type of function.
//
// Example:
// typedef F<T>: Function<A> Function<B>();
//
// We only use: Function, List, int (and function types).
// We import 'dart:core' directly and with prefix 'core'.
abstract class Printable {
/// Builds a descriptive string that can be used as an identifier.
///
/// The string is mainly used for disambiguation, and not for its readability.
void writeIdentifier(StringBuffer buffer);
}
abstract class TypeLike implements Printable {
/// Prints `this` as valid Dart code for a Type.
void writeType(StringBuffer buffer);
/// Whether this instance uses T in some way.
bool get usesT;
}
/// Provides a unique integer for every parameter in a function.
int parameterNameCounter = 0;
/// Whether `T` should be replaced with `int`.
bool shouldReplaceTWithInt = false;
class Parameter implements Printable {
final TypeLike type;
final String name;
Parameter(this.type, this.name);
// Type or name can be null.
@override
writeIdentifier(buffer) {
if (type == null) {
buffer.write("null");
} else {
type.writeIdentifier(buffer);
}
buffer.write("_");
buffer.write(name);
}
void writeType(StringBuffer buffer) {
assert(type != null || name != null);
if (name == null) {
type.writeType(buffer);
} else if (type == null) {
buffer.write(name);
} else {
type.writeType(buffer);
buffer.write(" ");
buffer.write(name);
}
}
void writeInFunction(StringBuffer buffer) {
assert(type != null || name != null);
if (name == null) {
type.writeType(buffer);
buffer.write(" x");
buffer.write(parameterNameCounter++);
} else if (type == null) {
buffer.write(name);
} else {
type.writeType(buffer);
buffer.write(" ");
buffer.write(name);
}
}
bool operator ==(other) {
return other is Parameter && name == other.name && type == other.type;
}
int get hashCode {
return ((name.hashCode * 37) ^ type.hashCode) & 0xFFFFFFFF;
}
bool get usesT => type?.usesT == true;
}
class GenericParameter implements TypeLike {
final String name;
final TypeLike bound;
GenericParameter(this.name, [this.bound]);
// Bound may be null.
@override
writeIdentifier(buffer) {
buffer.write(name);
buffer.write("_");
if (bound == null) {
buffer.write("null");
} else {
bound.writeIdentifier(buffer);
}
}
@override
writeType(buffer) {
buffer.write(name);
if (bound != null) {
buffer.write(" extends ");
bound.writeType(buffer);
}
}
bool operator ==(other) {
return other is GenericParameter &&
name == other.name &&
bound == other.bound;
}
int get hashCode {
return ((name.hashCode * 23) ^ bound.hashCode) & 0xFFFFFFFF;
}
bool get usesT {
return bound?.usesT == true;
}
}
void _describeList(StringBuffer buffer, List<Printable> list) {
if (list == null) {
buffer.write("0");
return;
}
buffer.write(list.length.toString());
buffer.write("_");
for (int i = 0; i < list.length; i++) {
if (i != 0) buffer.write("_");
list[i].writeIdentifier(buffer);
}
}
void _writeTypes(StringBuffer buffer, List<TypeLike> list,
[String prefix = "", String postfix = ""]) {
if (list == null || list.isEmpty) return;
buffer.write(prefix);
for (int i = 0; i < list.length; i++) {
if (i != 0) buffer.write(", ");
list[i].writeType(buffer);
}
buffer.write(postfix);
}
void _writeParameters(
StringBuffer buffer, List<Parameter> list, bool inFunction,
[String prefix = "", String postfix = ""]) {
if (list == null || list.isEmpty) return;
buffer.write(prefix);
for (int i = 0; i < list.length; i++) {
if (i != 0) buffer.write(", ");
if (inFunction) {
list[i].writeInFunction(buffer);
} else {
list[i].writeType(buffer);
}
}
buffer.write(postfix);
}
bool _listUsesT(List elements) {
if (elements == null) return false;
return elements.any((p) => p.usesT);
}
bool _listEquals(List list1, List list2) {
if (list1 == list2) return true; // Also covers both being null.
if (list1 == null || list2 == null) return false;
for (int i = 0; i < list1.length; i++) {
if (list1[i] != list2[i]) return false;
}
return true;
}
int _listHash(List list) {
if (list == null) return null.hashCode;
int result = 71;
for (int i = 0; i < list.length; i++) {
result = ((result * 11) ^ list[i].hashCode) & 0xFFFFFFFF;
}
return result;
}
class FunctionType implements TypeLike {
final TypeLike returnType;
final List<GenericParameter> generic;
final List<Parameter> required;
final List<Parameter> optional;
final List<Parameter> named;
FunctionType(this.returnType, this.generic, this.required,
[this.optional, this.named]);
@override
writeIdentifier(buffer) {
buffer.write("Fun_");
if (returnType == null) {
buffer.write("null");
} else {
returnType.writeIdentifier(buffer);
}
buffer.write("_");
_describeList(buffer, generic);
buffer.write("_");
_describeList(buffer, required);
buffer.write("_");
_describeList(buffer, optional);
buffer.write("_");
_describeList(buffer, named);
}
@override
writeType(buffer) {
if (returnType != null) {
returnType.writeType(buffer);
buffer.write(" ");
}
buffer.write("Function");
if (generic != null) _writeTypes(buffer, generic, "<", ">");
buffer.write("(");
bool notInFunction = true;
_writeParameters(buffer, required, notInFunction);
if ((optional != null || named != null) &&
required != null &&
required.isNotEmpty) {
buffer.write(", ");
}
_writeParameters(buffer, optional, notInFunction, "[", "]");
_writeParameters(buffer, named, notInFunction, "{", "}");
buffer.write(")");
}
/// Writes this type as if it was a function.
void writeFunction(StringBuffer buffer, String name, {bool replaceT: true}) {
shouldReplaceTWithInt = replaceT;
parameterNameCounter = 0;
if (returnType != null) {
returnType.writeType(buffer);
buffer.write(" ");
}
buffer.write(name);
if (generic != null) _writeTypes(buffer, generic, "<", ">");
buffer.write("(");
bool inFunction = true;
_writeParameters(buffer, required, inFunction);
if ((optional != null || named != null) &&
required != null &&
required.isNotEmpty) {
buffer.write(", ");
}
_writeParameters(buffer, optional, inFunction, "[", "]");
_writeParameters(buffer, named, inFunction, "{", "}");
buffer.write(") => null;");
shouldReplaceTWithInt = false;
}
bool operator ==(other) {
return returnType == other.returnType &&
_listEquals(generic, other.generic) &&
_listEquals(required, other.required) &&
_listEquals(optional, other.optional) &&
_listEquals(named, other.named);
}
int get hashCode {
return ((returnType.hashCode * 13) ^
(_listHash(generic) * 17) ^
(_listHash(required) * 53) ^
(_listHash(optional) ^ 31) ^
(_listHash(named) * 87)) &
0xFFFFFFFF;
}
bool get usesT {
return returnType?.usesT == true ||
[generic, required, optional, named].any(_listUsesT);
}
}
class NominalType implements TypeLike {
final String prefix;
final String name;
final List<TypeLike> generic;
NominalType(this.name, [this.prefix, this.generic]);
@override
writeIdentifier(buffer) {
buffer.write(prefix);
buffer.write("_");
buffer.write(name);
_describeList(buffer, generic);
}
@override
writeType(buffer) {
if (prefix != null && prefix != "") {
buffer.write(prefix);
buffer.write(".");
}
if (shouldReplaceTWithInt && name == "T") {
buffer.write("int");
} else {
buffer.write(name);
}
_writeTypes(buffer, generic, "<", ">");
}
bool operator ==(other) {
return other is NominalType && prefix == other.prefix && name == other.name;
}
int get hashCode {
return ((prefix.hashCode * 37) ^ name.hashCode) & 0xFFFFFFFF;
}
bool get usesT => name == "T" || _listUsesT(generic);
}
List<FunctionType> buildFunctionTypes() {
List<GenericParameter> as = [
new GenericParameter("A"),
// new GenericParameter("A", new NominalType("int")),
// new GenericParameter("A", new NominalType("int", "core")),
];
List<GenericParameter> bs = [
// new GenericParameter("B"),
// new GenericParameter("B", new NominalType("int")),
new GenericParameter("B", new NominalType("int", "core")),
];
List<TypeLike> basicTypes = [
new NominalType("int"),
// new NominalType("int", "core"),
// new NominalType("List"),
// new NominalType("List", "core"),
new NominalType("Function"),
new NominalType("List", "", [new NominalType("Function")]),
new NominalType("List", "core", [new NominalType("int", "core")]),
new NominalType("List", "", [new NominalType("T")]),
// new NominalType("List", "", [new NominalType("Function")]),
];
List<TypeLike> basicsPlusNull = [
basicTypes,
<TypeLike>[null]
].expand((x) => x).toList();
List<TypeLike> basicsPlusNullPlusVoid = [
basicsPlusNull,
[new NominalType("void")],
].expand((x) => x).toList();
List<TypeLike> basicsPlusNullPlusB = [
basicsPlusNull,
[
new NominalType("B"),
new NominalType("List", "", [new NominalType("B")])
]
].expand((x) => x).toList();
List<TypeLike> basicsPlusNullPlusBPlusVoid = [
basicsPlusNullPlusB,
[new NominalType("void")],
].expand((x) => x).toList();
List<TypeLike> basicsPlusNullPlusA = [
basicsPlusNull,
[
new NominalType("A"),
new NominalType("List", "", [new NominalType("A")])
]
].expand((x) => x).toList();
List<TypeLike> basicsPlusNullPlusAPlusVoid = [
basicsPlusNullPlusA,
[new NominalType("void")],
].expand((x) => x).toList();
List<TypeLike> buildFunctionTypes(TypeLike returnType, TypeLike parameterType,
[List<GenericParameter> generics,
bool generateMoreCombinations = false]) {
List<TypeLike> result = [];
if (parameterType == null) {
// int Function().
result.add(new FunctionType(returnType, generics, null));
return result;
}
// int Function(int x).
result.add(new FunctionType(
returnType, generics, [new Parameter(parameterType, "x")]));
if (!generateMoreCombinations) return result;
// int Function([int x]).
result.add(new FunctionType(
returnType, generics, null, [new Parameter(parameterType, "x")]));
// int Function(int, [int x])
result.add(new FunctionType(
returnType,
generics,
[new Parameter(new NominalType("int"), null)],
[new Parameter(parameterType, "x")]));
// int Function(int x, [int x])
result.add(new FunctionType(
returnType,
generics,
[new Parameter(new NominalType("int"), "y")],
[new Parameter(parameterType, "x")]));
// int Function(int);
result.add(new FunctionType(
returnType, generics, [new Parameter(parameterType, null)]));
// int Function([int]);
result.add(new FunctionType(
returnType, generics, null, [new Parameter(parameterType, null)]));
// int Function(int, [int])
result.add(new FunctionType(
returnType,
generics,
[new Parameter(new NominalType("int"), null)],
[new Parameter(parameterType, null)]));
// int Function(int x, [int])
result.add(new FunctionType(
returnType,
generics,
[new Parameter(new NominalType("int"), "x")],
[new Parameter(parameterType, null)]));
// int Function({int x}).
result.add(new FunctionType(returnType, generics, null, null,
[new Parameter(parameterType, "x")]));
// int Function(int, {int x})
result.add(new FunctionType(
returnType,
generics,
[new Parameter(new NominalType("int"), null)],
null,
[new Parameter(parameterType, "x")]));
// int Function(int x, {int x})
result.add(new FunctionType(
returnType,
generics,
[new Parameter(new NominalType("int"), "y")],
null,
[new Parameter(parameterType, "x")]));
return result;
}
// The "smaller" function types. May also be used non-nested.
List<TypeLike> functionTypes = [];
for (TypeLike returnType in basicsPlusNullPlusVoid) {
for (TypeLike parameterType in basicsPlusNull) {
bool generateMoreCombinations = true;
functionTypes.addAll(buildFunctionTypes(
returnType, parameterType, null, generateMoreCombinations));
}
}
// These use `B` from the generic type of the enclosing function.
List<TypeLike> returnFunctionTypesB = [];
for (TypeLike returnType in basicsPlusNullPlusBPlusVoid) {
TypeLike parameterType = new NominalType("B");
returnFunctionTypesB.addAll(buildFunctionTypes(returnType, parameterType));
}
for (TypeLike parameterType in basicsPlusNull) {
TypeLike returnType = new NominalType("B");
returnFunctionTypesB.addAll(buildFunctionTypes(returnType, parameterType));
}
for (TypeLike returnType in basicsPlusNullPlusAPlusVoid) {
for (TypeLike parameterType in basicsPlusNullPlusA) {
for (GenericParameter a in as) {
functionTypes
.addAll(buildFunctionTypes(returnType, parameterType, [a]));
}
}
}
List<TypeLike> types = [];
types.addAll(functionTypes);
// Now add some higher-order function types.
for (TypeLike returnType in functionTypes) {
types.addAll(buildFunctionTypes(returnType, null));
types.addAll(buildFunctionTypes(returnType, new NominalType("int")));
for (var b in bs) {
types.addAll(buildFunctionTypes(returnType, null, [b]));
types.addAll(buildFunctionTypes(returnType, new NominalType("int"), [b]));
}
}
for (TypeLike returnType in returnFunctionTypesB) {
for (var b in bs) {
types.addAll(buildFunctionTypes(returnType, null, [b]));
types.addAll(buildFunctionTypes(returnType, new NominalType("int"), [b]));
}
}
return types;
}
final String HEADER = """
// Copyright (c) 2016, 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.
// GENERATED - DON'T EDIT.
// GENERATED - DON'T EDIT.
// GENERATED - DON'T EDIT.
// GENERATED - DON'T EDIT.
// GENERATED - DON'T EDIT.
// GENERATED - DON'T EDIT.
// GENERATED - DON'T EDIT.
// GENERATED - DON'T EDIT.
import 'dart:core';
import 'dart:core' as core;
import 'package:expect/expect.dart';
@NoInline()
@AssumeDynamic()
confuse(f) => f;
final bool inCheckedMode =
(() { bool result = false; assert(result = true); return result; })();
""";
class Unit {
final String name;
final StringBuffer typedefs = new StringBuffer();
final StringBuffer globals = new StringBuffer();
final StringBuffer tests = new StringBuffer();
final StringBuffer fields = new StringBuffer();
final StringBuffer statics = new StringBuffer();
final StringBuffer testMethods = new StringBuffer();
final StringBuffer methods = new StringBuffer();
Unit(this.name);
void write(StringBuffer buffer) {
buffer.write("""
$HEADER
$typedefs
$globals
class $name<T> {
final bool tIsBool;
final bool tIsInt;
final bool tIsDynamic;
$fields
$name({this.tIsBool: false, this.tIsInt: false})
: tIsDynamic = !tIsBool && !tIsInt;
$methods
runTests() {\n$tests }
$testMethods
}
void main() {
new $name().runTests();
new $name<int>(tIsInt: true).runTests();
new $name<bool>(tIsBool: true).runTests();
}
""");
}
}
final TEST_METHOD_HEADER = """
void #testName() {
// #typeCode""";
// Tests that apply for every type.
final COMMON_TESTS_TEMPLATE = """
Expect.isTrue(#staticFunName is #typeName);
Expect.isTrue(confuse(#staticFunName) is #typeName);
// In checked mode, verifies the type.
#typeCode #localName;
// The static function #staticFunName sets `T` to `int`.
if (!tIsBool) {
#fieldName = #staticFunName as dynamic;
#localName = #staticFunName as dynamic;
#fieldName = confuse(#staticFunName);
#localName = confuse(#staticFunName);
}
Expect.isTrue(#methodFunName is #typeName);
Expect.isTrue(#methodFunName is #typeCode);
Expect.isTrue(confuse(#methodFunName) is #typeName);
// In checked mode, verifies the type.
#fieldName = #methodFunName;
#localName = #methodFunName;
#fieldName = confuse(#methodFunName);
#localName = confuse(#methodFunName);""";
// Tests that depend on the typedef "T" argument.
//
// These tests are executed when the surrounding class is instantiated with
// its generic type set to `int`, `dynamic` or `bool`. In the latter case, the
// class field `tIsBool` is set to true.
// While the types themselves are not affected by the class` `T`, the methods
// of the class may use `T`:
//
// For example:
// class A<T> {
// f(List<T> x) {}
// }
final TYPEDEF_T_TESTS_TEMPLATE = """
if (!tIsBool) {
Expect.isTrue(#staticFunName is #typeName<int>);
Expect.isFalse(#staticFunName is #typeName<bool>);
Expect.isTrue(confuse(#staticFunName) is #typeName<int>);
Expect.isFalse(confuse(#staticFunName) is #typeName<bool>);
Expect.equals(tIsDynamic, #methodFunName is #typeName<bool>);
Expect.equals(tIsDynamic, confuse(#methodFunName) is #typeName<bool>);
} else {
if (inCheckedMode) {
Expect.throws(() { #fieldName = (#staticFunName as dynamic); });
Expect.throws(() { #fieldName = confuse(#staticFunName); });
#typeCode #localName;
Expect.throws(() { #localName = (#staticFunName as dynamic); });
Expect.throws(() { #localName = confuse(#staticFunName); });
}
#typeCode #localName = #methodFunName;
// In checked mode, verifies the type.
#fieldName = #methodFunName;
#fieldName = confuse(#methodFunName);
}""";
final TEST_METHOD_FOOTER = " }";
String createTypeName(int id) => "F$id";
String createStaticFunName(int id) => "f$id";
String createMethodFunName(int id) => "m$id";
String createFieldName(int id) => "x$id";
String createLocalName(int id) => "l$id";
String createTestName(int id) => "test${createTypeName(id)}";
String createTypeCode(FunctionType type) {
StringBuffer typeBuffer = new StringBuffer();
type.writeType(typeBuffer);
return typeBuffer.toString();
}
String createStaticFunCode(FunctionType type, int id) {
StringBuffer staticFunBuffer = new StringBuffer();
type.writeFunction(staticFunBuffer, createStaticFunName(id));
return staticFunBuffer.toString();
}
String createMethodFunCode(FunctionType type, int id) {
StringBuffer methodFunBuffer = new StringBuffer();
type.writeFunction(methodFunBuffer, createMethodFunName(id), replaceT: false);
return methodFunBuffer.toString();
}
String createTestMethodFunCode(FunctionType type, String typeCode, int id) {
String fillTemplate(String template, int id) {
var result = template
.replaceAll("#typeName", createTypeName(id))
.replaceAll("#staticFunName", createStaticFunName(id))
.replaceAll("#methodFunName", createMethodFunName(id))
.replaceAll("#fieldName", createFieldName(id))
.replaceAll("#localName", createLocalName(id))
.replaceAll("#testName", createTestName(id))
.replaceAll("#typeCode", typeCode);
assert(!result.contains("#"));
return result;
}
String commonTests = fillTemplate(COMMON_TESTS_TEMPLATE, id);
String genericTTests = "";
if (type.usesT) {
genericTTests = fillTemplate(TYPEDEF_T_TESTS_TEMPLATE, id);
}
return """
${fillTemplate(TEST_METHOD_HEADER, id)}
$commonTests
$genericTTests
$TEST_METHOD_FOOTER
""";
}
void generateTests() {
// Keep methods and classes smaller by distributing over several different
// classes.
List<Unit> units = [];
for (int i = 0; i < 100; i++) {
units.add(new Unit("U$i"));
}
var types = buildFunctionTypes();
int typeCounter = 0;
types.forEach((FunctionType type) {
Unit unit = units[typeCounter % units.length];
String typeName = createTypeName(typeCounter);
String fieldName = createFieldName(typeCounter);
String testName = createTestName(typeCounter);
String typeCode = createTypeCode(type);
String staticFunCode = createStaticFunCode(type, typeCounter);
String methodFunCode = createMethodFunCode(type, typeCounter);
String testMethodCode =
createTestMethodFunCode(type, typeCode, typeCounter);
unit.typedefs.writeln("typedef $typeName<T> = $typeCode;");
unit.globals.writeln(staticFunCode);
unit.fields.writeln(" $typeCode $fieldName;");
unit.methods.writeln(" $methodFunCode");
unit.testMethods.writeln("$testMethodCode");
unit.tests.writeln(" $testName();");
typeCounter++;
});
for (int i = 0; i < units.length; i++) {
var unit = units[i];
var buffer = new StringBuffer();
unit.write(buffer);
var path = Platform.script.resolve("function_type${i}_test.dart").path;
new File(path).writeAsStringSync(buffer.toString());
}
}
void printUsage() {
print("""
Generates function type tests.
All tests are generated in the same directory as this script.
""");
}
void main(List<String> arguments) {
if (arguments.length != 0) {
printUsage();
return;
}
generateTests();
}

View file

@ -0,0 +1,42 @@
// Copyright (c) 2012, 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.
// Dart test for a function type test that cannot be eliminated at compile time.
import "package:expect/expect.dart";
class A {}
typedef int F();
typedef G = F; /// 00: compile-time error
typedef H = int; /// 01: compile-time error
typedef I = A; /// 02: compile-time error
typedef J = List<int>; /// 03: compile-time error
typedef K = Function(Function<A>(A
<int> /// 04: static type warning
));
typedef L = Function({
/* /// 05: compile-time error
bool
*/ /// 05: compile-time error
x});
typedef M = Function({
/* /// 06: compile-time error
bool
*/ /// 06: compile-time error
int});
foo({bool int}) {}
main() {
bool b = true;
Expect.isFalse(b is G); /// 00: continued
Expect.isFalse(b is H); /// 01: continued
Expect.isFalse(b is I); /// 02: continued
Expect.isFalse(b is J); /// 03: continued
Expect.isFalse(b is K); /// 04: continued
Expect.isFalse(b is L);
Expect.isFalse(b is M);
Expect.isTrue(foo is M);
}

View file

@ -0,0 +1,24 @@
// Copyright (c) 2012, 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.
// Dart test for a function type test that cannot be eliminated at compile time.
import "package:expect/expect.dart";
class A {
}
typedef F<T> = Function<S>(List<S> list, Function<A>(A), T);
foo(List<dynamic> x, bar(String y), int z) {}
foo2(List<int> x, bar(String y), int z) {}
main() {
Expect.isTrue(foo is F);
Expect.isTrue(foo is F<int>);
Expect.isFalse(foo is F<bool>);
Expect.isTrue(foo2 is F);
Expect.isTrue(foo2 is F<int>);
Expect.isFalse(foo2 is F<bool>);
}

View file

@ -66,7 +66,6 @@ vm/debug_break_enabled_vm_test/01: Crash, OK # Expected to hit breakpoint.
[ ($compiler == none || $compiler == precompiler || $compiler == app_jit) && $checked ]
type_variable_bounds4_test/01: Fail # Issue 14006
generalized_function_type_test: SkipSlow # Times out in checked mode. Generated test is too large.
[ ($compiler == none || $compiler == precompiler || $compiler == app_jit) && (($runtime == vm || $runtime == dart_precompiled) || $runtime == drt || $runtime == dartium) ]
dynamic_prefix_core_test/none: Fail # Issue 12478

View file

@ -50,7 +50,7 @@ const_for_in_variable_test/01: MissingCompileTimeError # Issue 25161
generic_function_typedef_test: Crash # Issue 27969
generic_function_typedef2_test: Crash # Issue 27969
generalized_function_type_test: Crash # Issue 27969
function_type/function_type*: Crash # Issue 27969
# Please add new failing tests before this line.
# Section below is for invalid tests.

View file

@ -37,6 +37,12 @@ accessor_conflict_import_test: RuntimeError # Issue 25626
duplicate_part_test/01: MissingCompileTimeError # Issue 27517
bad_typedef_test/00: Crash # Issue 28214
generic_function_typedef2_test/00: Crash # Issue 28214
generic_function_typedef2_test/01: Crash # Issue 28214
generic_function_typedef2_test/02: Crash # Issue 28214
generic_function_typedef2_test/03: Crash # Issue 28214
generic_function_typedef2_test/05: Crash # Issue 28214
generic_function_typedef2_test/06: Crash # Issue 28214
covariant_test/02: MissingCompileTimeError, OK # Accepts `covariant` for statics/top-level.
covariant_test/08: MissingCompileTimeError, OK # Accepts `covariant` for statics/top-level.

View file

@ -45,6 +45,9 @@ vm/debug_break_enabled_vm_test/none: CompileTimeError
vm/reflect_core_vm_test: CompileTimeError
vm/regress_27201_test: CompileTimeError
vm/regress_28325_test: RuntimeError # Issue 28055.
generic_function_typedef2_test: DartkCrash # Issue 27969
generic_function_typedef_test: DartkCrash # Issue 27969
function_type/function_type*: DartkCrash # Issue 27969
# dartk: JIT failures
[ $compiler == dartk && $runtime == vm ]