Index: pkg/front_end/lib/src/fasta/kernel/kernel_library_builder.dart |
diff --git a/pkg/front_end/lib/src/fasta/kernel/kernel_library_builder.dart b/pkg/front_end/lib/src/fasta/kernel/kernel_library_builder.dart |
index 1cecfe5d279023dada6cc9d568d0bce27534c61e..64739bd3bd31e72328d700e019bdab7a6a2134b7 100644 |
--- a/pkg/front_end/lib/src/fasta/kernel/kernel_library_builder.dart |
+++ b/pkg/front_end/lib/src/fasta/kernel/kernel_library_builder.dart |
@@ -14,7 +14,8 @@ import '../errors.dart' show internalError; |
import '../loader.dart' show Loader; |
-import '../modifier.dart' show abstractMask, staticMask; |
+import '../modifier.dart' |
+ show abstractMask, namedMixinApplicationMask, staticMask; |
import '../source/source_library_builder.dart' |
show DeclarationBuilder, SourceLibraryBuilder; |
@@ -41,7 +42,6 @@ import 'kernel_builder.dart' |
KernelFunctionTypeBuilder, |
KernelInvalidTypeBuilder, |
KernelMixinApplicationBuilder, |
- KernelNamedMixinApplicationBuilder, |
KernelNamedTypeBuilder, |
KernelProcedureBuilder, |
KernelTypeBuilder, |
@@ -49,7 +49,7 @@ import 'kernel_builder.dart' |
LibraryBuilder, |
MemberBuilder, |
MetadataBuilder, |
- NamedMixinApplicationBuilder, |
+ NamedTypeBuilder, |
PrefixBuilder, |
ProcedureBuilder, |
Scope, |
@@ -104,10 +104,12 @@ class KernelLibraryBuilder |
KernelTypeBuilder supertype, |
List<KernelTypeBuilder> interfaces, |
int charOffset) { |
- assert(currentDeclaration.parent == libraryDeclaration); |
- Map<String, MemberBuilder> members = currentDeclaration.members; |
- Map<String, MemberBuilder> constructors = currentDeclaration.constructors; |
- Map<String, MemberBuilder> setters = currentDeclaration.setters; |
+ // Nested declaration began in `OutlineBuilder.beginClassDeclaration`. |
+ var declaration = endNestedDeclaration()..resolveTypes(typeVariables, this); |
+ assert(declaration.parent == libraryDeclaration); |
+ Map<String, MemberBuilder> members = declaration.members; |
+ Map<String, MemberBuilder> constructors = declaration.constructors; |
+ Map<String, MemberBuilder> setters = declaration.setters; |
Scope classScope = new Scope( |
members, setters, scope.withTypeVariables(typeVariables), |
@@ -122,7 +124,8 @@ class KernelLibraryBuilder |
modifiers, |
className, |
typeVariables, |
- supertype, |
+ applyMixins(supertype, |
+ subclassName: className, typeVariables: typeVariables), |
interfaces, |
classScope, |
constructorScope, |
@@ -130,6 +133,8 @@ class KernelLibraryBuilder |
new List<ConstructorReferenceBuilder>.from(constructorReferences), |
charOffset); |
constructorReferences.clear(); |
+ Map<String, TypeVariableBuilder> typeVariablesByName = |
+ checkTypeVariables(typeVariables, cls); |
void setParent(String name, MemberBuilder member) { |
while (member != null) { |
member.parent = cls; |
@@ -137,14 +142,305 @@ class KernelLibraryBuilder |
} |
} |
- members.forEach(setParent); |
- constructors.forEach(setParent); |
+ void setParentAndCheckConflicts(String name, MemberBuilder member) { |
+ if (typeVariablesByName != null) { |
+ TypeVariableBuilder tv = typeVariablesByName[name]; |
+ if (tv != null) { |
+ cls.addCompileTimeError( |
+ member.charOffset, "Conflict with type variable '$name'."); |
+ cls.addCompileTimeError(tv.charOffset, "This is the type variable."); |
+ } |
+ } |
+ setParent(name, member); |
+ } |
+ |
+ members.forEach(setParentAndCheckConflicts); |
+ constructors.forEach(setParentAndCheckConflicts); |
+ // Formally, a setter has the name `id=`, so it can never conflict with a |
+ // type variable. |
setters.forEach(setParent); |
- // Nested declaration began in `OutlineBuilder.beginClassDeclaration`. |
- endNestedDeclaration().resolveTypes(typeVariables, this); |
addBuilder(className, cls, charOffset); |
} |
+ Map<String, TypeVariableBuilder> checkTypeVariables( |
+ List<TypeVariableBuilder> typeVariables, Builder owner) { |
+ if (typeVariables?.isEmpty ?? true) return null; |
+ Map<String, TypeVariableBuilder> typeVariablesByName = |
+ <String, TypeVariableBuilder>{}; |
+ for (TypeVariableBuilder tv in typeVariables) { |
+ TypeVariableBuilder existing = typeVariablesByName[tv.name]; |
+ if (existing != null) { |
+ addCompileTimeError(tv.charOffset, |
+ "A type variable can't have the same name as another."); |
+ addCompileTimeError( |
+ existing.charOffset, "The other type variable named '${tv.name}'."); |
+ } else { |
+ typeVariablesByName[tv.name] = tv; |
+ if (owner is ClassBuilder) { |
+ // Only classes and type variables can't have the same name. See |
+ // [#29555](https://github.com/dart-lang/sdk/issues/29555). |
+ if (tv.name == owner.name) { |
+ addCompileTimeError( |
+ tv.charOffset, |
+ "A type variable can't have the same name as its enclosing " |
+ "declaration."); |
+ } |
+ } |
+ } |
+ } |
+ return typeVariablesByName; |
+ } |
+ |
+ KernelTypeBuilder applyMixin( |
+ KernelTypeBuilder supertype, KernelTypeBuilder mixin, String signature, |
+ {List<MetadataBuilder> metadata, |
+ String name, |
+ List<TypeVariableBuilder> typeVariables, |
+ int modifiers: abstractMask, |
+ List<KernelTypeBuilder> interfaces, |
+ int charOffset: -1}) { |
+ var constructors = <String, MemberBuilder>{}; |
+ bool isNamed = name != null; |
+ SourceClassBuilder builder; |
+ if (isNamed) { |
+ modifiers |= namedMixinApplicationMask; |
+ } else { |
+ name = supertype.name; |
+ int index = name.indexOf("^"); |
+ if (index != -1) { |
+ name = name.substring(0, index); |
+ } |
+ name = "$name&${mixin.name}$signature"; |
+ builder = mixinApplicationClasses[name]; |
+ } |
+ if (builder == null) { |
+ builder = new SourceClassBuilder( |
+ metadata, |
+ modifiers, |
+ name, |
+ typeVariables, |
+ supertype, |
+ interfaces, |
+ new Scope(<String, MemberBuilder>{}, <String, MemberBuilder>{}, |
+ scope.withTypeVariables(typeVariables), |
+ isModifiable: false), |
+ new Scope(constructors, null, null, isModifiable: false), |
+ this, |
+ <ConstructorReferenceBuilder>[], |
+ charOffset, |
+ null, |
+ mixin); |
+ addBuilder(name, builder, charOffset); |
+ if (!isNamed) { |
+ mixinApplicationClasses[name] = builder; |
+ } |
+ } |
+ return addNamedType(name, <KernelTypeBuilder>[], charOffset) |
+ ..bind(isNamed ? builder : null); |
+ } |
+ |
+ KernelTypeBuilder applyMixins(KernelTypeBuilder type, |
+ {List<MetadataBuilder> metadata, |
+ String name, |
+ String subclassName, |
+ List<TypeVariableBuilder> typeVariables, |
+ int modifiers: abstractMask, |
+ List<KernelTypeBuilder> interfaces, |
+ int charOffset: -1}) { |
+ if (type is KernelMixinApplicationBuilder) { |
+ subclassName ??= name; |
+ List<List<String>> signatureParts = <List<String>>[]; |
+ Map<String, String> unresolved = <String, String>{}; |
+ Map<String, String> unresolvedReversed = <String, String>{}; |
+ int unresolvedCount = 0; |
+ Map<String, TypeBuilder> freeTypes = <String, TypeBuilder>{}; |
+ |
+ if (name == null || type.mixins.length != 1) { |
+ TypeBuilder last = type.mixins.last; |
+ |
+ /// Compute a signature of the type arguments used by the supertype and |
+ /// mixins. These types are free variables. At this point we can't |
+ /// trust that the number of type arguments match the type parameters, |
+ /// so we also need to be able to detect missing type arguments. To do |
+ /// so, we separate each list of type arguments by `^` and type |
+ /// arguments by `&`. For example, the mixin `C<S> with M<T, U>` would |
+ /// look like this: |
+ /// |
+ /// ^#U0^#U1&#U2 |
+ /// |
+ /// Where `#U0`, `#U1`, and `#U2` are the free variables arising from |
+ /// `S`, `T`, and `U` respectively. |
+ /// |
+ /// As we can resolve any type parameters used at this point, those are |
+ /// named `#T0` and so forth. This reduces the number of free variables |
+ /// which is crucial for memory usage and the Dart VM's bootstrap |
+ /// sequence. |
+ /// |
+ /// For example, consider this use of mixin applications: |
+ /// |
+ /// class _InternalLinkedHashMap<K, V> extends _HashVMBase |
+ /// with |
+ /// MapMixin<K, V>, |
+ /// _LinkedHashMapMixin<K, V>, |
+ /// _HashBase, |
+ /// _OperatorEqualsAndHashCode {} |
+ /// |
+ /// In this case, only two variables are free, and we produce this |
+ /// signature: `^^#T0&#T1^#T0&#T1^^`. Assume another class uses the |
+ /// sames mixins but with missing type arguments for `MapMixin`, its |
+ /// signature would be: `^^^#T0&#T1^^`. |
+ /// |
+ /// Note that we do not need to compute a signature for a named mixin |
+ /// application with only one mixin as we don't have to invent a name |
+ /// for any classes in this situation. |
+ void analyzeArguments(TypeBuilder type) { |
+ if (name != null && type == last) { |
+ // The last mixin of a named mixin application doesn't contribute |
+ // to free variables. |
+ return; |
+ } |
+ if (type is NamedTypeBuilder) { |
+ List<String> part = <String>[]; |
+ for (int i = 0; i < (type.arguments?.length ?? 0); i++) { |
+ var argument = type.arguments[i]; |
+ String name; |
+ if (argument is NamedTypeBuilder) { |
+ if (argument.builder != null) { |
+ int index = typeVariables?.indexOf(argument.builder) ?? -1; |
+ if (index != -1) { |
+ name = "#T${index}"; |
+ } |
+ } else if (argument.arguments == null) { |
+ name = unresolved[argument.name] ??= "#U${unresolvedCount++}"; |
+ } |
+ } |
+ name ??= "#U${unresolvedCount++}"; |
+ unresolvedReversed[name] = argument.name; |
+ freeTypes[name] = argument; |
+ part.add(name); |
+ type.arguments[i] = |
+ new KernelNamedTypeBuilder(name, null, -1, fileUri); |
+ } |
+ signatureParts.add(part); |
+ } |
+ } |
+ |
+ analyzeArguments(type.supertype); |
+ type.mixins.forEach(analyzeArguments); |
+ } |
+ KernelTypeBuilder supertype = type.supertype; |
+ List<List<String>> currentSignatureParts = <List<String>>[]; |
+ int currentSignatureCount = 0; |
+ String computeSignature() { |
+ if (freeTypes.isEmpty) return ""; |
+ currentSignatureParts.add(signatureParts[currentSignatureCount++]); |
+ if (currentSignatureParts.any((l) => l.isNotEmpty)) { |
+ return "^${currentSignatureParts.map((l) => l.join('&')).join('^')}"; |
+ } else { |
+ return ""; |
+ } |
+ } |
+ |
+ Map<String, TypeVariableBuilder> computeTypeVariables() { |
+ Map<String, TypeVariableBuilder> variables = |
+ <String, TypeVariableBuilder>{}; |
+ for (List<String> strings in currentSignatureParts) { |
+ for (String name in strings) { |
+ variables[name] ??= addTypeVariable(name, null, -1); |
+ } |
+ } |
+ return variables; |
+ } |
+ |
+ checkArguments(t) { |
+ for (var argument in t.arguments ?? const []) { |
+ if (argument.builder == null && argument.name.startsWith("#")) { |
+ throw "No builder on ${argument.name}"; |
+ } |
+ } |
+ } |
+ |
+ computeSignature(); // This combines the supertype with the first mixin. |
+ for (int i = 0; i < type.mixins.length - 1; i++) { |
+ Set<String> supertypeArguments = new Set<String>(); |
+ for (var part in currentSignatureParts) { |
+ supertypeArguments.addAll(part); |
+ } |
+ String signature = computeSignature(); |
+ var variables = computeTypeVariables(); |
+ if (supertypeArguments.isNotEmpty) { |
+ supertype = addNamedType( |
+ supertype.name, |
+ supertypeArguments |
+ .map((n) => addNamedType(n, null, -1)..bind(variables[n])) |
+ .toList(), |
+ -1); |
+ } |
+ KernelNamedTypeBuilder mixin = type.mixins[i]; |
+ for (var type in mixin.arguments ?? const []) { |
+ type.bind(variables[type.name]); |
+ } |
+ checkArguments(supertype); |
+ checkArguments(mixin); |
+ supertype = applyMixin(supertype, mixin, signature, |
+ typeVariables: |
+ new List<TypeVariableBuilder>.from(variables.values)); |
+ } |
+ KernelNamedTypeBuilder mixin = type.mixins.last; |
+ |
+ Set<String> supertypeArguments = new Set<String>(); |
+ for (var part in currentSignatureParts) { |
+ supertypeArguments.addAll(part); |
+ } |
+ String signature = name == null ? computeSignature() : ""; |
+ var variables; |
+ if (name == null) { |
+ variables = computeTypeVariables(); |
+ typeVariables = new List<TypeVariableBuilder>.from(variables.values); |
+ if (supertypeArguments.isNotEmpty) { |
+ supertype = addNamedType( |
+ supertype.name, |
+ supertypeArguments |
+ .map((n) => addNamedType(n, null, -1)..bind(variables[n])) |
+ .toList(), |
+ -1); |
+ } |
+ } else { |
+ if (supertypeArguments.isNotEmpty) { |
+ supertype = addNamedType(supertype.name, |
+ supertypeArguments.map((n) => freeTypes[n]).toList(), -1); |
+ } |
+ } |
+ |
+ if (name == null) { |
+ for (var type in mixin.arguments ?? const []) { |
+ type.bind(variables[type.name]); |
+ } |
+ } |
+ checkArguments(supertype); |
+ checkArguments(mixin); |
+ |
+ KernelNamedTypeBuilder t = applyMixin(supertype, mixin, signature, |
+ metadata: metadata, |
+ name: name, |
+ typeVariables: typeVariables, |
+ modifiers: modifiers, |
+ interfaces: interfaces, |
+ charOffset: charOffset); |
+ if (name == null) { |
+ var builder = t.builder; |
+ t = addNamedType( |
+ t.name, freeTypes.keys.map((k) => freeTypes[k]).toList(), -1); |
+ if (builder != null) { |
+ t.bind(builder); |
+ } |
+ } |
+ return t; |
+ } else { |
+ return type; |
+ } |
+ } |
+ |
void addNamedMixinApplication( |
List<MetadataBuilder> metadata, |
String name, |
@@ -153,12 +449,16 @@ class KernelLibraryBuilder |
KernelTypeBuilder mixinApplication, |
List<KernelTypeBuilder> interfaces, |
int charOffset) { |
- NamedMixinApplicationBuilder builder = |
- new KernelNamedMixinApplicationBuilder(metadata, name, typeVariables, |
- modifiers, mixinApplication, interfaces, this, charOffset); |
// Nested declaration began in `OutlineBuilder.beginNamedMixinApplication`. |
endNestedDeclaration().resolveTypes(typeVariables, this); |
- addBuilder(name, builder, charOffset); |
+ KernelNamedTypeBuilder supertype = applyMixins(mixinApplication, |
+ metadata: metadata, |
+ name: name, |
+ typeVariables: typeVariables, |
+ modifiers: modifiers, |
+ interfaces: interfaces, |
+ charOffset: charOffset); |
+ checkTypeVariables(typeVariables, supertype.builder); |
} |
void addField(List<MetadataBuilder> metadata, int modifiers, |
@@ -542,9 +842,16 @@ class KernelLibraryBuilder |
@override |
void includePart(covariant KernelLibraryBuilder part) { |
+ part.mixinApplicationClasses |
+ .forEach((String name, SourceClassBuilder builder) { |
+ SourceClassBuilder existing = |
+ mixinApplicationClasses.putIfAbsent(name, () => builder); |
+ if (existing != builder) { |
+ part.scope.local.remove(name); |
+ } |
+ }); |
super.includePart(part); |
nativeMethods.addAll(part.nativeMethods); |
boundlessTypeVariables.addAll(part.boundlessTypeVariables); |
- assert(mixinApplicationClasses.isEmpty); |
} |
} |