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['']; |