| Index: frog/type.dart
|
| diff --git a/frog/type.dart b/frog/type.dart
|
| index b83bce83c65fc411757e56ecef88b3e5a610e398..e36976ddd6e8ba4b82c18eb99450ef3089b90832 100644
|
| --- a/frog/type.dart
|
| +++ b/frog/type.dart
|
| @@ -740,7 +740,7 @@ class DefinedType extends Type {
|
| void set parent(Type p) { _parent = p; }
|
|
|
| List<Type> interfaces;
|
| - Type factory_;
|
| + DefinedType defaultType;
|
|
|
| Set<Type> directSubtypes;
|
| Set<Type> _subtypes;
|
| @@ -984,9 +984,9 @@ class DefinedType extends Type {
|
| }
|
| }
|
| this.interfaces = _resolveInterfaces(typeDef.implementsTypes);
|
| - if (typeDef.factoryType != null) {
|
| - world.error('factory not allowed on classes',
|
| - typeDef.factoryType.span);
|
| + if (typeDef.defaultType != null) {
|
| + world.error('default not allowed on classes',
|
| + typeDef.defaultType.span);
|
| }
|
| } else {
|
| if (typeDef.implementsTypes != null &&
|
| @@ -1001,11 +1001,13 @@ class DefinedType extends Type {
|
| typeDef.extendsTypes[res].span);
|
| }
|
|
|
| - if (typeDef.factoryType != null) {
|
| - factory_ = resolveType(typeDef.factoryType, true);
|
| - if (factory_ == null) {
|
| + if (typeDef.defaultType != null) {
|
| + defaultType = resolveType(typeDef.defaultType.baseType, true);
|
| + if (defaultType == null) {
|
| // TODO(jimhug): Appropriate warning levels;
|
| - world.warning('unresolved factory', typeDef.factoryType.span);
|
| + world.warning('unresolved default class', typeDef.defaultType.span);
|
| + } else {
|
| + defaultType._resolveTypeParams(typeDef.defaultType.typeParameters);
|
| }
|
| }
|
| }
|
| @@ -1014,12 +1016,7 @@ class DefinedType extends Type {
|
| this.interfaces = [world.functionType];
|
| }
|
|
|
| - if (typeParameters != null) {
|
| - for (var tp in typeParameters) {
|
| - tp.enclosingElement = this;
|
| - tp.resolve();
|
| - }
|
| - }
|
| + _resolveTypeParams(typeParameters);
|
|
|
| if (isObject) _createNotEqualMember();
|
|
|
| @@ -1038,6 +1035,14 @@ class DefinedType extends Type {
|
| }
|
| }
|
|
|
| + _resolveTypeParams(List<ParameterType> params) {
|
| + if (params == null) return;
|
| + for (var tp in params) {
|
| + tp.enclosingElement = this;
|
| + tp.resolve();
|
| + }
|
| + }
|
| +
|
| addMethod(String methodName, FunctionDefinition definition) {
|
| if (methodName == null) methodName = definition.name.name;
|
|
|
| @@ -1147,8 +1152,10 @@ class DefinedType extends Type {
|
| getConstructor(String constructorName) {
|
| var ret = constructors[constructorName];
|
| if (ret != null) {
|
| - if (factory_ != null) {
|
| - return factory_.getFactory(this, constructorName);
|
| + if (defaultType != null) {
|
| + // TODO(jmesserly): only need to check once.
|
| + _checkDefaultTypeParams();
|
| + return defaultType.getFactory(this, constructorName);
|
| }
|
| return ret;
|
| }
|
| @@ -1158,6 +1165,55 @@ class DefinedType extends Type {
|
| return _tryCreateDefaultConstructor(constructorName);
|
| }
|
|
|
| + /**
|
| + * Checks that default type parameters match between all 3 locations:
|
| + * 1. the interface (this)
|
| + * 2. the "default" type parameters
|
| + * 3. the class's type parameters
|
| + *
|
| + * The only deviation is that 2 and 3 can have a tighter "extends" bound.
|
| + */
|
| + _checkDefaultTypeParams() {
|
| + // Convert null to empty list so it doesn't complicate the logic
|
| + List<ParameterType> toList(list) => (list != null ? list : const []);
|
| +
|
| + TypeDefinition typeDef = definition;
|
| + if (typeDef.defaultType.oldFactory) {
|
| + // TODO(jmesserly): for now skip checking of old factories
|
| + return;
|
| + }
|
| +
|
| + var interfaceParams = toList(typeParameters);
|
| + var defaultParams = toList(typeDef.defaultType.typeParameters);
|
| + var classParams = toList(defaultType.typeParameters);
|
| +
|
| + if (interfaceParams.length != defaultParams.length
|
| + || defaultParams.length != classParams.length) {
|
| + world.error('"default" must have the same number of type parameters as '
|
| + + 'the class and interface do', span, typeDef.defaultType.span,
|
| + defaultType.span);
|
| + return;
|
| + }
|
| +
|
| + for (int i = 0; i < interfaceParams.length; i++) {
|
| + var ip = interfaceParams[i];
|
| + var dp = defaultParams[i];
|
| + var cp = classParams[i];
|
| + dp.resolve();
|
| + if (ip.name != dp.name || dp.name != cp.name) {
|
| + world.error('default class must have the same type parameter names as '
|
| + + 'the class and interface', ip.span, dp.span, cp.span);
|
| + } else if (dp.extendsType != cp.extendsType) {
|
| + world.error('default class type parameters must have the same extends '
|
| + + 'as the class does', dp.span, cp.span);
|
| + } else if (!dp.extendsType.isSubtypeOf(ip.extendsType)) {
|
| + // TODO(jmesserly): left this as a warning; it seems harmless to me
|
| + world.warning('"default" can only have tighter type parameter "extends"'
|
| + + ' than the interface', dp.span, ip.span);
|
| + }
|
| + }
|
| + }
|
| +
|
| _tryCreateDefaultConstructor(String name) {
|
| // Check if we can create a default constructor.
|
| if (name == '' && definition != null && isClass &&
|
| @@ -1175,7 +1231,7 @@ class DefinedType extends Type {
|
|
|
| TypeDefinition typeDef = definition;
|
| var c = new FunctionDefinition(null, null, typeDef.name, [],
|
| - null, inits, native, body, span);
|
| + inits, native, body, span);
|
| addMethod(null, c);
|
| constructors[''].resolve();
|
| return constructors[''];
|
|
|