From e401b6f18aca53860f0807d1ee8e13edbc7bbaa0 Mon Sep 17 00:00:00 2001 From: Johnni Winther Date: Fri, 8 Sep 2023 08:32:25 +0000 Subject: [PATCH] [kernel] Add sealed classes GenericDeclaration and GenericFunction This adds a typed `TypeParameter.declaration` property to be used instead of the `parent` property. The `declaration` property holds the declaration (Class, Method, Extension, etc.) that introduced the type parameter. `GenericFunction` is the subset of `GenericDeclaration` that is defined through a `FunctionNode`. TEST=existing Change-Id: Ie89e7f5fa12a7966507a250cacc098eb0ce6b30b Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/323160 Reviewed-by: Mayank Patke Reviewed-by: Jens Johansen Commit-Queue: Johnni Winther Reviewed-by: Aske Simon Christensen Reviewed-by: Alexander Markov --- pkg/compiler/lib/src/ir/closure.dart | 46 +++++----- pkg/compiler/lib/src/ir/scope_visitor.dart | 8 +- pkg/compiler/lib/src/ir/visitors.dart | 2 +- .../lib/src/js_model/element_map_impl.dart | 19 ++-- .../lib/src/kernel/element_map_impl.dart | 47 ++++------ pkg/compiler/lib/src/serialization/sink.dart | 21 +++-- pkg/dart2wasm/lib/closures.dart | 8 +- pkg/dart2wasm/lib/code_generator.dart | 5 +- pkg/dart2wasm/lib/types.dart | 2 +- .../lib/src/kernel/type_table.dart | 2 +- .../lib/src/fasta/kernel/body_builder.dart | 5 +- pkg/front_end/lib/src/fasta/kernel/utils.dart | 2 +- .../source/source_constructor_builder.dart | 7 +- .../lib/src/fasta/source/source_loader.dart | 1 + .../text_representation/data/constants.dart | 5 +- .../data/constants_opt_out.dart | 2 +- .../text_representation/data/expressions.dart | 12 ++- .../test/text_representation/data/types.dart | 42 +++++++-- .../data/types_opt_out.dart | 24 +++-- pkg/kernel/lib/ast.dart | 87 +++++++++++++++++-- pkg/kernel/lib/binary/ast_from_binary.dart | 8 +- pkg/kernel/lib/src/text_util.dart | 47 ++++++---- pkg/kernel/lib/text/ast_to_text.dart | 54 +++++++----- pkg/kernel/lib/text/indentation.dart | 4 +- pkg/kernel/lib/type_algebra.dart | 5 +- pkg/kernel/lib/verifier.dart | 12 ++- pkg/kernel/pubspec.yaml | 2 +- pkg/kernel/test/verify_test.dart | 14 +-- .../type_flow/summary_collector.dart | 4 +- .../type_flow/transformer.dart | 6 +- 30 files changed, 320 insertions(+), 183 deletions(-) diff --git a/pkg/compiler/lib/src/ir/closure.dart b/pkg/compiler/lib/src/ir/closure.dart index 88df3b316f3..99c4a8c9fb3 100644 --- a/pkg/compiler/lib/src/ir/closure.dart +++ b/pkg/compiler/lib/src/ir/closure.dart @@ -344,41 +344,35 @@ class TypeVariableTypeWithContext implements ir.Node { factory TypeVariableTypeWithContext( ir.TypeParameterType type, ir.TreeNode? context) { TypeVariableKind kind; - ir.TreeNode? typeDeclaration = type.parameter.parent; + ir.GenericDeclaration? typeDeclaration = type.parameter.declaration; + // TODO(fishythefish): Use exhaustive pattern switch. if (typeDeclaration == null) { // We have a function type variable, like `T` in `void Function(int)`. kind = TypeVariableKind.function; } else if (typeDeclaration is ir.Class) { // We have a class type variable, like `T` in `class Class { ... }`. kind = TypeVariableKind.cls; - } else { - final parent = typeDeclaration.parent; - if (parent is ir.Member) { - ir.Member member = parent; - if (member is ir.Constructor || - (member is ir.Procedure && member.isFactory)) { - // We have a synthesized generic method type variable for a class type - // variable. - // TODO(johnniwinther): Handle constructor/factory type variables as - // method type variables. - kind = TypeVariableKind.cls; - typeDeclaration = member.enclosingClass; - } else { - // We have a generic method type variable, like `T` in - // `m() { ... }`. - kind = TypeVariableKind.method; - typeDeclaration = parent; - context = typeDeclaration; - } + } else if (typeDeclaration is ir.Procedure) { + if (typeDeclaration.isFactory) { + // We have a synthesized generic method type variable for a class type + // variable. + // TODO(johnniwinther): Handle constructor/factory type variables as + // method type variables. + kind = TypeVariableKind.cls; + typeDeclaration = typeDeclaration.enclosingClass; } else { - // We have a generic local function type variable, like `T` in - // `m() { local() { ... } ... }`. - assert(parent is ir.LocalFunction, - "Unexpected type declaration: $typeDeclaration"); - kind = TypeVariableKind.local; - typeDeclaration = parent; + // We have a generic method type variable, like `T` in + // `m() { ... }`. + kind = TypeVariableKind.method; context = typeDeclaration; } + } else { + // We have a generic local function type variable, like `T` in + // `m() { local() { ... } ... }`. + assert(typeDeclaration is ir.LocalFunction, + "Unexpected type declaration: $typeDeclaration"); + kind = TypeVariableKind.local; + context = typeDeclaration; } return TypeVariableTypeWithContext.internal( type, context, kind, typeDeclaration); diff --git a/pkg/compiler/lib/src/ir/scope_visitor.dart b/pkg/compiler/lib/src/ir/scope_visitor.dart index 3882bd19b5a..0407c336c2d 100644 --- a/pkg/compiler/lib/src/ir/scope_visitor.dart +++ b/pkg/compiler/lib/src/ir/scope_visitor.dart @@ -416,10 +416,10 @@ class ScopeModelBuilder extends ir.Visitor TypeVariableTypeWithContext( ir.TypeParameterType.withDefaultNullabilityForLibrary( typeParameter, library), - // If this typeParameter is part of a typedef then its parent is - // null because it has no context. Just pass in null for the - // context in that case. - typeParameter.parent?.parent); + // If this typeParameter is part of a function type then its + // declaration is null because it has no context. Just pass in null + // for the context in that case. + typeParameter.declaration); ir.TreeNode? context = _executableContext; if (_isInsideClosure && context is ir.Procedure && context.isFactory) { diff --git a/pkg/compiler/lib/src/ir/visitors.dart b/pkg/compiler/lib/src/ir/visitors.dart index 20da699b902..8557c88e7dc 100644 --- a/pkg/compiler/lib/src/ir/visitors.dart +++ b/pkg/compiler/lib/src/ir/visitors.dart @@ -103,7 +103,7 @@ class DartTypeConverter extends ir.DartTypeVisitor { if (typeParameter != null) { return _convertNullability(typeParameter, node); } - if (node.parameter.parent is ir.Typedef) { + if (node.parameter.declaration is ir.Typedef) { // Typedefs are only used in type literals so we never need their type // variables. return _dartTypes.dynamicType(); diff --git a/pkg/compiler/lib/src/js_model/element_map_impl.dart b/pkg/compiler/lib/src/js_model/element_map_impl.dart index 0d2cdf2bc27..0e179178ec2 100644 --- a/pkg/compiler/lib/src/js_model/element_map_impl.dart +++ b/pkg/compiler/lib/src/js_model/element_map_impl.dart @@ -1592,20 +1592,13 @@ class JsKernelToElementMap implements JsToElementMap, IrToElementMap { TypeVariableEntity getTypeVariableInternal(ir.TypeParameter node) { TypeVariableEntity? typeVariable = typeVariableMap[node]; if (typeVariable == null) { - final parent = node.parent; - if (parent is ir.FunctionNode) { - final member = parent.parent; - int index = parent.typeParameters.indexOf(node); - if (member is ir.Constructor) { - ir.Class cls = member.enclosingClass; + final declaration = node.declaration; + if (declaration is ir.Procedure) { + int index = declaration.typeParameters.indexOf(node); + if (declaration.kind == ir.ProcedureKind.Factory) { + ir.Class cls = declaration.enclosingClass!; typeVariableMap[node] = typeVariable = getTypeVariableInternal(cls.typeParameters[index]); - } else if (member is ir.Procedure) { - if (member.kind == ir.ProcedureKind.Factory) { - ir.Class cls = member.enclosingClass!; - typeVariableMap[node] = typeVariable = - getTypeVariableInternal(cls.typeParameters[index]); - } } } } @@ -1613,7 +1606,7 @@ class JsKernelToElementMap implements JsToElementMap, IrToElementMap { throw failedAt( CURRENT_ELEMENT_SPANNABLE, "No type variable entity for $node on " - "${node.parent is ir.FunctionNode ? node.parent!.parent : node.parent}"); + "${node.declaration}"); } return typeVariable; } diff --git a/pkg/compiler/lib/src/kernel/element_map_impl.dart b/pkg/compiler/lib/src/kernel/element_map_impl.dart index 3cbe138478b..112cca6db24 100644 --- a/pkg/compiler/lib/src/kernel/element_map_impl.dart +++ b/pkg/compiler/lib/src/kernel/element_map_impl.dart @@ -1234,41 +1234,28 @@ class KernelToElementMap implements IrToElementMap { !envIsClosed, "Environment of $this is closed. Trying to create " "type variable for $node."); - final parent = node.parent; - if (parent is ir.Class) { - ir.Class cls = parent; - int index = cls.typeParameters.indexOf(node); + final declaration = node.declaration; + // TODO(fishythefish): Use exhaustive pattern switch. + if (declaration is ir.Class) { + int index = declaration.typeParameters.indexOf(node); return typeVariables.register( - createTypeVariable(getClassInternal(cls), node.name!, index), + createTypeVariable(getClassInternal(declaration), node.name!, index), KTypeVariableData(node)); - } - if (parent is ir.FunctionNode) { - ir.FunctionNode func = parent; - int index = func.typeParameters.indexOf(node); - final funcParent = func.parent; - if (funcParent is ir.Constructor) { - ir.Constructor constructor = funcParent; - ir.Class cls = constructor.enclosingClass; + } else if (declaration is ir.Procedure) { + int index = declaration.typeParameters.indexOf(node); + if (declaration.kind == ir.ProcedureKind.Factory) { + ir.Class cls = declaration.enclosingClass!; return getTypeVariableInternal(cls.typeParameters[index]); - } else if (funcParent is ir.Procedure) { - ir.Procedure procedure = funcParent; - if (procedure.kind == ir.ProcedureKind.Factory) { - ir.Class cls = procedure.enclosingClass!; - return getTypeVariableInternal(cls.typeParameters[index]); - } else { - return typeVariables.register( - createTypeVariable( - getMethodInternal(procedure), node.name!, index), - KTypeVariableData(node)); - } - } else if (funcParent is ir.LocalFunction) { - // Ensure that local function type variables have been created. - getLocalFunction(funcParent); - return typeVariableMap[node]; } else { - throw UnsupportedError('Unsupported function type parameter parent ' - 'node ${func.parent}.'); + return typeVariables.register( + createTypeVariable( + getMethodInternal(declaration), node.name!, index), + KTypeVariableData(node)); } + } else if (declaration is ir.LocalFunction) { + // Ensure that local function type variables have been created. + getLocalFunction(declaration); + return typeVariableMap[node]; } throw UnsupportedError('Unsupported type parameter type node $node.'); } diff --git a/pkg/compiler/lib/src/serialization/sink.dart b/pkg/compiler/lib/src/serialization/sink.dart index 2a917e03a87..d6a90f1002c 100644 --- a/pkg/compiler/lib/src/serialization/sink.dart +++ b/pkg/compiler/lib/src/serialization/sink.dart @@ -595,18 +595,23 @@ class DataSinkWriter { } void _writeTypeParameter(ir.TypeParameter value, MemberData? memberData) { - ir.TreeNode parent = value.parent!; - if (parent is ir.Class) { + ir.GenericDeclaration declaration = value.declaration!; + // TODO(fishythefish): Use exhaustive pattern switch. + if (declaration is ir.Class) { _sinkWriter.writeEnum(_TypeParameterKind.cls); - _writeClassNode(parent); - _sinkWriter.writeInt(parent.typeParameters.indexOf(value)); - } else if (parent is ir.FunctionNode) { + _writeClassNode(declaration); + _sinkWriter.writeInt(declaration.typeParameters.indexOf(value)); + } else if (declaration is ir.Procedure) { _sinkWriter.writeEnum(_TypeParameterKind.functionNode); - _writeFunctionNode(parent, memberData); - _sinkWriter.writeInt(parent.typeParameters.indexOf(value)); + _writeFunctionNode(declaration.function, memberData); + _sinkWriter.writeInt(declaration.typeParameters.indexOf(value)); + } else if (declaration is ir.LocalFunction) { + _sinkWriter.writeEnum(_TypeParameterKind.functionNode); + _writeFunctionNode(declaration.function, memberData); + _sinkWriter.writeInt(declaration.typeParameters.indexOf(value)); } else { throw UnsupportedError( - "Unsupported TypeParameter parent ${parent.runtimeType}"); + "Unsupported TypeParameter declaration ${declaration.runtimeType}"); } } diff --git a/pkg/dart2wasm/lib/closures.dart b/pkg/dart2wasm/lib/closures.dart index 862cba444f3..3e874dbeb6d 100644 --- a/pkg/dart2wasm/lib/closures.dart +++ b/pkg/dart2wasm/lib/closures.dart @@ -1131,7 +1131,7 @@ class CaptureFinder extends RecursiveVisitor { @override void visitTypeParameter(TypeParameter node) { - if (node.parent is FunctionNode) { + if (node.declaration is GenericFunction) { if (depth > 0) { variableDepth[node] = depth; } @@ -1196,10 +1196,10 @@ class CaptureFinder extends RecursiveVisitor { void visitTypeParameterType(TypeParameterType node) { if (member is Constructor) { _visitVariableUse(node.parameter); - } else if (node.parameter.parent != null && - node.parameter.parent == member.enclosingClass) { + } else if (node.parameter.declaration != null && + node.parameter.declaration == member.enclosingClass) { _visitThis(); - } else if (node.parameter.parent is FunctionNode) { + } else if (node.parameter.declaration is GenericFunction) { _visitVariableUse(node.parameter); } super.visitTypeParameterType(node); diff --git a/pkg/dart2wasm/lib/code_generator.dart b/pkg/dart2wasm/lib/code_generator.dart index 9441057ada4..3dec0c8e388 100644 --- a/pkg/dart2wasm/lib/code_generator.dart +++ b/pkg/dart2wasm/lib/code_generator.dart @@ -3057,7 +3057,8 @@ class CodeGenerator extends ExpressionVisitor1 w.ValueType resultType; // `this` will not be initialized yet for constructor initializer lists - if (parameter.parent is FunctionNode || reference.isInitializerReference) { + if (parameter.declaration is GenericFunction || + reference.isInitializerReference) { // Type argument to function w.Local? local = typeLocals[parameter]; if (local != null) { @@ -3071,7 +3072,7 @@ class CodeGenerator extends ExpressionVisitor1 } } else { // Type argument of class - Class cls = parameter.parent as Class; + Class cls = parameter.declaration as Class; ClassInfo info = translator.classInfo[cls]!; int fieldIndex = translator.typeParameterIndex[parameter]!; visitThis(info.nonNullableType); diff --git a/pkg/dart2wasm/lib/types.dart b/pkg/dart2wasm/lib/types.dart index df4504c0374..2626ee0ea37 100644 --- a/pkg/dart2wasm/lib/types.dart +++ b/pkg/dart2wasm/lib/types.dart @@ -334,7 +334,7 @@ class Types { } bool isFunctionTypeParameter(TypeParameterType type) => - type.parameter.parent == null; + type.parameter.declaration == null; bool _isTypeConstant(DartType type) { return type is DynamicType || diff --git a/pkg/dev_compiler/lib/src/kernel/type_table.dart b/pkg/dev_compiler/lib/src/kernel/type_table.dart index f2eee6234cd..0ad4f5b04f3 100644 --- a/pkg/dev_compiler/lib/src/kernel/type_table.dart +++ b/pkg/dev_compiler/lib/src/kernel/type_table.dart @@ -223,7 +223,7 @@ class TypeTable { // readability to little or no benefit. It would be good to do this // when we know that we can hoist it to an outer scope, but for // now we just disable it. - if (freeVariables.any((i) => i.parent is FunctionNode)) { + if (freeVariables.any((i) => i.declaration is GenericFunction)) { return true; } diff --git a/pkg/front_end/lib/src/fasta/kernel/body_builder.dart b/pkg/front_end/lib/src/fasta/kernel/body_builder.dart index 2898086845f..5fe872dd917 100644 --- a/pkg/front_end/lib/src/fasta/kernel/body_builder.dart +++ b/pkg/front_end/lib/src/fasta/kernel/body_builder.dart @@ -9007,8 +9007,9 @@ class BodyBuilder extends StackListenerImpl TypeVariableBuilder typeParameterBuilder = declaration as TypeVariableBuilder; TypeParameter typeParameter = typeParameterBuilder.parameter; - if (typeParameter.parent is Class || - typeParameter.parent is Extension) { + if (typeParameter.declaration is Class || + typeParameter.declaration is Extension || + typeParameter.declaration is ExtensionTypeDeclaration) { if (constantContext != ConstantContext.none && (!inConstructorInitializer || !allowPotentiallyConstantType)) { LocatedMessage message = diff --git a/pkg/front_end/lib/src/fasta/kernel/utils.dart b/pkg/front_end/lib/src/fasta/kernel/utils.dart index 8cf4400e3a4..61660c8d107 100644 --- a/pkg/front_end/lib/src/fasta/kernel/utils.dart +++ b/pkg/front_end/lib/src/fasta/kernel/utils.dart @@ -159,7 +159,7 @@ Component createExpressionEvaluationComponent(Procedure procedure) { Map typeSubstitution = {}; for (TypeParameter typeParam in realClass.typeParameters) { TypeParameter newNode = new TypeParameter(typeParam.name) - ..parent = fakeClass; + ..declaration = fakeClass; typeParams[typeParam] = newNode; typeSubstitution[typeParam] = new TypeParameterType.forAlphaRenaming(typeParam, newNode); diff --git a/pkg/front_end/lib/src/fasta/source/source_constructor_builder.dart b/pkg/front_end/lib/src/fasta/source/source_constructor_builder.dart index 40d5336ee7a..03dd0e77531 100644 --- a/pkg/front_end/lib/src/fasta/source/source_constructor_builder.dart +++ b/pkg/front_end/lib/src/fasta/source/source_constructor_builder.dart @@ -404,7 +404,12 @@ class DeclaredSourceConstructorBuilder super.classBuilder as SourceClassBuilder; @override - Member get readTarget => _constructorTearOff ?? _constructor; + Member get readTarget => + _constructorTearOff ?? + // The case is need to ensure that the upper bound is [Member] and not + // [GenericFunction]. + // ignore: unnecessary_cast + _constructor as Member; @override Member? get writeTarget => null; diff --git a/pkg/front_end/lib/src/fasta/source/source_loader.dart b/pkg/front_end/lib/src/fasta/source/source_loader.dart index ccf6ceec158..86df28f9a65 100644 --- a/pkg/front_end/lib/src/fasta/source/source_loader.dart +++ b/pkg/front_end/lib/src/fasta/source/source_loader.dart @@ -1316,6 +1316,7 @@ severity: $severity builder, dietListener.memberScope, thisVariable: extensionThis); + builder.procedure.function = parameters..parent = builder.procedure; for (VariableDeclaration variable in parameters.positionalParameters) { listener.typeInferrer.assignedVariables.declare(variable); } diff --git a/pkg/front_end/test/text_representation/data/constants.dart b/pkg/front_end/test/text_representation/data/constants.dart index 9aecf7f4af0..0c6d875eb88 100644 --- a/pkg/front_end/test/text_representation/data/constants.dart +++ b/pkg/front_end/test/text_representation/data/constants.dart @@ -4,7 +4,10 @@ /*library: nnbd=true*/ -T id(T /*T%*/ t) => t; +T id( + T /*normal|limited.id.T%*/ /*verbose.library org-dartlang-test:///a/b/c/main.dart::id.T%*/ + t) => + t; class Class1 { const Class1(); diff --git a/pkg/front_end/test/text_representation/data/constants_opt_out.dart b/pkg/front_end/test/text_representation/data/constants_opt_out.dart index 1c9f0cdf6be..a338dfd2f67 100644 --- a/pkg/front_end/test/text_representation/data/constants_opt_out.dart +++ b/pkg/front_end/test/text_representation/data/constants_opt_out.dart @@ -7,7 +7,7 @@ /*library: nnbd=false*/ library test; -T id(T /*T**/ t) => t; +T id(T /*normal|limited.id.T**/ /*verbose.test::id.T**/ t) => t; class Class1 { const Class1(); diff --git a/pkg/front_end/test/text_representation/data/expressions.dart b/pkg/front_end/test/text_representation/data/expressions.dart index bf8c23c0146..fd5aebb3179 100644 --- a/pkg/front_end/test/text_representation/data/expressions.dart +++ b/pkg/front_end/test/text_representation/data/expressions.dart @@ -70,8 +70,16 @@ get topLevelGetter => 42; set topLevelSetter(/*dynamic*/ _) {} topLevelMethod() {} -T genericTopLevelMethod1(T /*T%*/ t) => t; -T genericTopLevelMethod2(T /*T%*/ t, S /*S%*/ s) => t; +T genericTopLevelMethod1( + T /*normal|limited.genericTopLevelMethod1.T%*/ /*verbose.expressions::genericTopLevelMethod1.T%*/ + t) => + t; +T genericTopLevelMethod2( + T /*normal|limited.genericTopLevelMethod2.T%*/ /*verbose.expressions::genericTopLevelMethod2.T%*/ + t, + S /*normal|limited.genericTopLevelMethod2.S%*/ /*verbose.expressions::genericTopLevelMethod2.S%*/ + s) => + t; /*member: exprNullLiteral:null*/ exprNullLiteral() => null; diff --git a/pkg/front_end/test/text_representation/data/types.dart b/pkg/front_end/test/text_representation/data/types.dart index 4145fe9fb3e..b90771e3c4c 100644 --- a/pkg/front_end/test/text_representation/data/types.dart +++ b/pkg/front_end/test/text_representation/data/types.dart @@ -71,14 +71,44 @@ futureOrType( /*normal|limited.FutureOr?*/ /*verbose.FutureOr?*/ o4) {} -typeVariableType1(T /*T%*/ o1, T? /*T?*/ o2) {} -typeVariableType2(T /*T*/ o1, T? /*T?*/ o2) {} +typeVariableType1( + T /*normal|limited.typeVariableType1.T%*/ /*verbose.test::typeVariableType1.T%*/ + o1, + T? /*normal|limited.typeVariableType1.T?*/ /*verbose.test::typeVariableType1.T?*/ + o2) {} +typeVariableType2( + T /*normal|limited.typeVariableType2.T*/ /*verbose.test::typeVariableType2.T*/ + o1, + T? /*normal|limited.typeVariableType2.T?*/ /*verbose.test::typeVariableType2.T?*/ + o2) {} typeVariableType3( - T /*T%*/ o1, S /*S%*/ p1, T? /*T?*/ o2, S? /*S?*/ p2) {} + T /*normal|limited.typeVariableType3.T%*/ /*verbose.test::typeVariableType3.T%*/ + o1, + S /*normal|limited.typeVariableType3.S%*/ /*verbose.test::typeVariableType3.S%*/ + p1, + T? /*normal|limited.typeVariableType3.T?*/ /*verbose.test::typeVariableType3.T?*/ + o2, + S? /*normal|limited.typeVariableType3.S?*/ /*verbose.test::typeVariableType3.S?*/ + p2) {} typeVariableType4( - T /*T%*/ o1, S /*S%*/ p1, T? /*T?*/ o2, S? /*S?*/ p2) {} -typeVariableType5(T /*T*/ o1, T? /*T?*/ o2) {} -typeVariableType6(T /*T%*/ o1, T? /*T?*/ o2) {} + T /*normal|limited.typeVariableType4.T%*/ /*verbose.test::typeVariableType4.T%*/ + o1, + S /*normal|limited.typeVariableType4.S%*/ /*verbose.test::typeVariableType4.S%*/ + p1, + T? /*normal|limited.typeVariableType4.T?*/ /*verbose.test::typeVariableType4.T?*/ + o2, + S? /*normal|limited.typeVariableType4.S?*/ /*verbose.test::typeVariableType4.S?*/ + p2) {} +typeVariableType5( + T /*normal|limited.typeVariableType5.T*/ /*verbose.test::typeVariableType5.T*/ + o1, + T? /*normal|limited.typeVariableType5.T?*/ /*verbose.test::typeVariableType5.T?*/ + o2) {} +typeVariableType6( + T /*normal|limited.typeVariableType6.T%*/ /*verbose.test::typeVariableType6.T%*/ + o1, + T? /*normal|limited.typeVariableType6.T?*/ /*verbose.test::typeVariableType6.T?*/ + o2) {} functionType1(void Function() /*void Function()*/ o1, void Function()? /*void Function()?*/ o2) {} functionType2( diff --git a/pkg/front_end/test/text_representation/data/types_opt_out.dart b/pkg/front_end/test/text_representation/data/types_opt_out.dart index 94c86e809d5..29383bc4ef0 100644 --- a/pkg/front_end/test/text_representation/data/types_opt_out.dart +++ b/pkg/front_end/test/text_representation/data/types_opt_out.dart @@ -42,11 +42,25 @@ futureOrType( /*normal|limited.FutureOr**/ /*verbose.FutureOr**/ o) {} -typeVariableType1(T /*T**/ o) {} -typeVariableType2(T /*T**/ o) {} -typeVariableType3(T /*T**/ o, S /*S**/ p) {} -typeVariableType4(T /*T**/ o, S /*S**/ p) {} -typeVariableType5(T /*T**/ o) {} +typeVariableType1( + T /*normal|limited.typeVariableType1.T**/ /*verbose.test::typeVariableType1.T**/ + o) {} +typeVariableType2( + T /*normal|limited.typeVariableType2.T**/ /*verbose.test::typeVariableType2.T**/ + o) {} +typeVariableType3( + T /*normal|limited.typeVariableType3.T**/ /*verbose.test::typeVariableType3.T**/ + o, + S /*normal|limited.typeVariableType3.S**/ /*verbose.test::typeVariableType3.S**/ + p) {} +typeVariableType4( + T /*normal|limited.typeVariableType4.T**/ /*verbose.test::typeVariableType4.T**/ + o, + S /*normal|limited.typeVariableType4.S**/ /*verbose.test::typeVariableType4.S**/ + p) {} +typeVariableType5( + T /*normal|limited.typeVariableType5.T**/ /*verbose.test::typeVariableType5.T**/ + o) {} functionType1(void Function() /*void Function()**/ o) {} functionType2( int Function(int) diff --git a/pkg/kernel/lib/ast.dart b/pkg/kernel/lib/ast.dart index 4d690648c30..b0745c2e1e3 100644 --- a/pkg/kernel/lib/ast.dart +++ b/pkg/kernel/lib/ast.dart @@ -846,7 +846,8 @@ class Combinator extends TreeNode { } /// Declaration of a type alias. -class Typedef extends NamedNode implements FileUriNode, Annotatable { +class Typedef extends NamedNode + implements FileUriNode, Annotatable, GenericDeclaration { /// The URI of the source file that contains the declaration of this typedef. @override Uri fileUri; @@ -855,7 +856,10 @@ class Typedef extends NamedNode implements FileUriNode, Annotatable { List annotations = const []; String name; + + @override final List typeParameters; + // TODO(johnniwinther): Make this non-nullable. DartType? type; @@ -974,13 +978,26 @@ class DirtifyingList extends ListBase { } } +/// Declaration that can introduce [TypeParameter]s. +sealed class GenericDeclaration implements TreeNode { + /// The type parameters introduced by this declaration. + List get typeParameters; +} + +/// Functions that can introduce [TypeParameter]s. +sealed class GenericFunction implements GenericDeclaration { + /// The [FunctionNode] that holds the introduced [typeParameters]. + FunctionNode get function; +} + /// Declaration of a regular class or a mixin application. /// /// Mixin applications may not contain fields or procedures, as they implicitly /// use those from its mixed-in type. However, the IR does not enforce this /// rule directly, as doing so can obstruct transformations. It is possible to /// transform a mixin application to become a regular class, and vice versa. -class Class extends NamedNode implements Annotatable, FileUriNode { +class Class extends NamedNode + implements Annotatable, FileUriNode, GenericDeclaration { /// Start offset of the class in the source file it comes from. /// /// Note that this includes annotations if any. @@ -1168,6 +1185,7 @@ class Class extends NamedNode implements Annotatable, FileUriNode { @override Uri fileUri; + @override final List typeParameters; /// The immediate super type, or `null` if this is the root class. @@ -1517,7 +1535,8 @@ class Class extends NamedNode implements Annotatable, FileUriNode { /// /// The members are converted into top-level procedures and only accessible /// by reference in the [Extension] node. -class Extension extends NamedNode implements Annotatable, FileUriNode { +class Extension extends NamedNode + implements Annotatable, FileUriNode, GenericDeclaration { /// Name of the extension. /// /// If unnamed, the extension will be given a synthesized name by the @@ -1529,6 +1548,7 @@ class Extension extends NamedNode implements Annotatable, FileUriNode { Uri fileUri; /// Type parameters declared on the extension. + @override final List typeParameters; /// The type in the 'on clause' of the extension declaration. @@ -1731,7 +1751,7 @@ class ExtensionMemberDescriptor { /// The members are converted into top-level procedures and only accessible /// by reference in the [ExtensionTypeDeclaration] node. class ExtensionTypeDeclaration extends NamedNode - implements Annotatable, FileUriNode { + implements Annotatable, FileUriNode, GenericDeclaration { /// Name of the extension type declaration. String name; @@ -1740,6 +1760,7 @@ class ExtensionTypeDeclaration extends NamedNode Uri fileUri; /// Type parameters declared on the extension. + @override final List typeParameters; /// The type in the underlying representation of the extension type @@ -2801,7 +2822,7 @@ enum ProcedureStubKind { /// For index-getters/setters, this is `[]` and `[]=`. /// For operators, this is the token for the operator, e.g. `+` or `==`, /// except for the unary minus operator, whose name is `unary-`. -class Procedure extends Member { +class Procedure extends Member implements GenericFunction { /// Start offset of the function in the source file it comes from. /// /// Note that this includes annotations if any. @@ -2909,6 +2930,9 @@ class Procedure extends Member { "$memberSignatureOrigin for $this."); } + @override + List get typeParameters => function.typeParameters; + // The function node's body might be lazily loaded, meaning that this value // might not be set correctly yet. Make sure the body is loaded before // returning anything. @@ -8402,7 +8426,8 @@ class AwaitExpression extends Expression { } /// Common super-interface for [FunctionExpression] and [FunctionDeclaration]. -abstract class LocalFunction implements TreeNode { +abstract class LocalFunction implements GenericFunction { + @override FunctionNode get function; } @@ -8417,6 +8442,9 @@ class FunctionExpression extends Expression implements LocalFunction { function.parent = this; } + @override + List get typeParameters => function.typeParameters; + @override DartType getStaticTypeInternal(StaticTypeContext context) { return function.computeFunctionType(context.nonNullable); @@ -10597,6 +10625,9 @@ class FunctionDeclaration extends Statement implements LocalFunction { function.parent = this; } + @override + List get typeParameters => function.typeParameters; + @override R accept(StatementVisitor v) => v.visitFunctionDeclaration(this); @@ -12237,7 +12268,7 @@ class TypeParameterType extends DartType { } else if (other is TypeParameterType) { if (nullability != other.nullability) return false; if (parameter != other.parameter) { - if (parameter.parent == null) { + if (parameter.isFunctionTypeTypeParameter) { // Function type parameters are also equal by assumption. if (assumptions == null) { return false; @@ -12632,6 +12663,46 @@ class TypeParameter extends TreeNode implements Annotatable { // Must match serialized bit positions. static const int FlagCovariantByClass = 1 << 0; + @Deprecated("Used TypeParameter.declaration instead.") + @override + TreeNode? get parent; + + @Deprecated("Used TypeParameter.declaration instead.") + @override + void set parent(TreeNode? value); + + GenericDeclaration? get declaration { + // TODO(johnniwinther): Store the declaration directly when [parent] is + // removed. + TreeNode? parent = super.parent; + if (parent is GenericDeclaration) { + return parent; + } else if (parent is FunctionNode) { + return parent.parent as GenericDeclaration; + } + assert( + parent == null, + "Unexpected type parameter parent node " + "${parent} (${parent.runtimeType})."); + return null; + } + + void set declaration(GenericDeclaration? value) { + switch (value) { + case Typedef(): + case Class(): + case Extension(): + case ExtensionTypeDeclaration(): + super.parent = value; + case Procedure(): + super.parent = value.function; + case LocalFunction(): + super.parent = value.function; + case null: + super.parent = null; + } + } + /// If this [TypeParameter] is a type parameter of a generic method, indicates /// whether the method implementation needs to contain a runtime type check to /// deal with generic covariance. @@ -12693,7 +12764,7 @@ class TypeParameter extends TreeNode implements Annotatable { printer.writeTypeParameterName(this); } - bool get isFunctionTypeTypeParameter => parent == null; + bool get isFunctionTypeTypeParameter => declaration == null; } class Supertype extends Node { diff --git a/pkg/kernel/lib/binary/ast_from_binary.dart b/pkg/kernel/lib/binary/ast_from_binary.dart index 8c3ffb1514d..d28be4fe9bf 100644 --- a/pkg/kernel/lib/binary/ast_from_binary.dart +++ b/pkg/kernel/lib/binary/ast_from_binary.dart @@ -3936,7 +3936,7 @@ class BinaryBuilder { } List readAndPushTypeParameterList( - [List? list, TreeNode? parent]) { + [List? list, GenericDeclaration? declaration]) { int length = readUInt30(); if (length == 0) { if (list != null) return list; @@ -3947,12 +3947,12 @@ class BinaryBuilder { } } if (list == null) { - list = new List.generate( - length, (_) => new TypeParameter(null, null)..parent = parent, + list = new List.generate(length, + (_) => new TypeParameter(null, null)..declaration = declaration, growable: useGrowableLists); } else if (list.length != length) { for (int i = 0; i < length; ++i) { - list.add(new TypeParameter(null, null)..parent = parent); + list.add(new TypeParameter(null, null)..declaration = declaration); } } typeParameterStack.addAll(list); diff --git a/pkg/kernel/lib/src/text_util.dart b/pkg/kernel/lib/src/text_util.dart index 53b1b338c1b..f6151112666 100644 --- a/pkg/kernel/lib/src/text_util.dart +++ b/pkg/kernel/lib/src/text_util.dart @@ -267,24 +267,37 @@ String memberNameToString(Member node) { String qualifiedTypeParameterNameToString(TypeParameter node, {bool includeLibraryName = false}) { - TreeNode? parent = node.parent; - if (parent is Class) { - return qualifiedClassNameToString(parent, - includeLibraryName: includeLibraryName) + - '.' + - typeParameterNameToString(node); - } else if (parent is Extension) { - return qualifiedExtensionNameToString(parent, - includeLibraryName: includeLibraryName) + - '.' + - typeParameterNameToString(node); - } else if (parent is Member) { - return qualifiedMemberNameToString(parent, - includeLibraryName: includeLibraryName) + - '.' + - typeParameterNameToString(node); + GenericDeclaration? declaration = node.declaration; + switch (declaration) { + case Class(): + return qualifiedClassNameToString(declaration, + includeLibraryName: includeLibraryName) + + '.' + + typeParameterNameToString(node); + case Extension(): + return qualifiedExtensionNameToString(declaration, + includeLibraryName: includeLibraryName) + + '.' + + typeParameterNameToString(node); + case ExtensionTypeDeclaration(): + return qualifiedExtensionTypeDeclarationNameToString(declaration, + includeLibraryName: includeLibraryName) + + '.' + + typeParameterNameToString(node); + case Typedef(): + return qualifiedTypedefNameToString(declaration, + includeLibraryName: includeLibraryName) + + '.' + + typeParameterNameToString(node); + case Procedure(): + return qualifiedMemberNameToString(declaration, + includeLibraryName: includeLibraryName) + + '.' + + typeParameterNameToString(node); + case LocalFunction(): + case null: + return typeParameterNameToString(node); } - return typeParameterNameToString(node); } String typeParameterNameToString(TypeParameter node) { diff --git a/pkg/kernel/lib/text/ast_to_text.dart b/pkg/kernel/lib/text/ast_to_text.dart index 5cb98a27467..a656ee2f81c 100644 --- a/pkg/kernel/lib/text/ast_to_text.dart +++ b/pkg/kernel/lib/text/ast_to_text.dart @@ -11,7 +11,7 @@ import '../ast.dart'; import '../import_table.dart'; import '../src/text_util.dart'; -abstract class Namer { +abstract mixin class Namer { int index = 0; final Map map = {}; @@ -143,18 +143,24 @@ String debugTypeParameterName(TypeParameter node) { } String debugQualifiedTypeParameterName(TypeParameter node) { - TreeNode? parent = node.parent; - if (parent is Class) { - return debugQualifiedClassName(parent) + - '::' + - debugTypeParameterName(node); + GenericDeclaration? declaration = node.declaration; + switch (declaration) { + case Class(): + return debugQualifiedClassName(declaration) + + '::' + + debugTypeParameterName(node); + case Procedure(): + return debugQualifiedMemberName(declaration) + + '::' + + debugTypeParameterName(node); + case Typedef(): + case Extension(): + case ExtensionTypeDeclaration(): + // TODO(johnniwinther): Support these cases directly? + case LocalFunction(): + case null: + return debugTypeParameterName(node); } - if (parent is Member) { - return debugQualifiedMemberName(parent) + - '::' + - debugTypeParameterName(node); - } - return debugTypeParameterName(node); } String debugVariableDeclarationName(VariableDeclaration node) { @@ -396,15 +402,21 @@ class Printer extends Visitor with VisitorVoidMixin { String getTypeParameterReference(TypeParameter node) { String name = getTypeParameterName(node); - TreeNode? parent = node.parent; - if (parent is FunctionNode && parent.parent is Member) { - String member = getMemberReference(parent.parent as Member); - return '$member::$name'; - } else if (parent is Class) { - String className = getClassReference(parent); - return '$className::$name'; - } else { - return name; // Bound inside a function type. + GenericDeclaration? declaration = node.declaration; + switch (declaration) { + case Class(): + String className = getClassReference(declaration); + return '$className::$name'; + case Procedure(): + String member = getMemberReference(declaration); + return '$member::$name'; + case Typedef(): + case Extension(): + case ExtensionTypeDeclaration(): + case LocalFunction(): + // TODO(johnniwinther): Support these cases correctly. + case null: + return name; // Bound inside a function type. } } diff --git a/pkg/kernel/lib/text/indentation.dart b/pkg/kernel/lib/text/indentation.dart index f16a4ac32a8..8808867fcf0 100644 --- a/pkg/kernel/lib/text/indentation.dart +++ b/pkg/kernel/lib/text/indentation.dart @@ -3,7 +3,7 @@ // BSD-style license that can be found in the LICENSE file. /// Indentation utility class. Should be used as a mixin in most cases. -class Indentation { +mixin class Indentation { /// The current indentation string. String get indentation { // Lazily add new indentation strings as required. @@ -50,7 +50,7 @@ class Indentation { } } -abstract class Tagging implements Indentation { +abstract mixin class Tagging implements Indentation { StringBuffer sb = new StringBuffer(); List tagStack = []; diff --git a/pkg/kernel/lib/type_algebra.dart b/pkg/kernel/lib/type_algebra.dart index 24683910f1d..ebe278734cb 100644 --- a/pkg/kernel/lib/type_algebra.dart +++ b/pkg/kernel/lib/type_algebra.dart @@ -463,7 +463,7 @@ class _ClassBottomSubstitution extends Substitution { @override DartType? getSubstitute(TypeParameter parameter, bool upperBound) { - if (parameter.parent == class_) { + if (parameter.declaration == class_) { return upperBound ? const NeverType.nonNullable() : const DynamicType(); } return null; @@ -1044,7 +1044,8 @@ class _FreeFunctionTypeVariableVisitor implements DartTypeVisitor { @override bool visitTypeParameterType(TypeParameterType node) { - return node.parameter.parent == null && !variables.contains(node.parameter); + return node.parameter.declaration == null && + !variables.contains(node.parameter); } @override diff --git a/pkg/kernel/lib/verifier.dart b/pkg/kernel/lib/verifier.dart index f78244d38ed..e418b74aff3 100644 --- a/pkg/kernel/lib/verifier.dart +++ b/pkg/kernel/lib/verifier.dart @@ -747,7 +747,7 @@ class VerifyingVisitor extends RecursiveResultVisitor { void visitFunctionType(FunctionType node) { if (node.typeParameters.isNotEmpty) { for (TypeParameter typeParameter in node.typeParameters) { - if (typeParameter.parent != null) { + if (typeParameter.declaration != null) { problem( localContext, "Type parameters of function types shouldn't have parents: " @@ -1188,20 +1188,18 @@ class VerifyingVisitor extends RecursiveResultVisitor { @override void visitTypeParameterType(TypeParameterType node) { TypeParameter parameter = node.parameter; + GenericDeclaration? declaration = parameter.declaration; if (!typeParametersInScope.contains(parameter)) { - TreeNode? owner = parameter.parent is FunctionNode - ? parameter.parent!.parent - : parameter.parent; problem( currentParent, "Type parameter '$parameter' referenced out of" - " scope, owner is: '${owner}'."); + " scope, declaration is: '${declaration}'."); } - if (parameter.parent is Class && !classTypeParametersAreInScope) { + if (declaration is Class && !classTypeParametersAreInScope) { problem( currentParent, "Type parameter '$parameter' referenced from" - " static context, parent is: '${parameter.parent}'."); + " static context, declaration is: '${parameter.declaration}'."); } } diff --git a/pkg/kernel/pubspec.yaml b/pkg/kernel/pubspec.yaml index de38fccb4b0..69851314c66 100644 --- a/pkg/kernel/pubspec.yaml +++ b/pkg/kernel/pubspec.yaml @@ -6,7 +6,7 @@ name: kernel publish_to: none environment: - sdk: '>=2.19.0 <3.0.0' + sdk: ^3.0.0 # Use 'any' constraints here; we get our versions from the DEPS file. dev_dependencies: diff --git a/pkg/kernel/test/verify_test.dart b/pkg/kernel/test/verify_test.dart index 84843f7434a..3be917a474c 100644 --- a/pkg/kernel/test/verify_test.dart +++ b/pkg/kernel/test/verify_test.dart @@ -144,7 +144,7 @@ void main() { }, (Node? node, Node? parent) => "${errorPrefix}Type parameter '$node' referenced out of scope," - " owner is: '$parent'.", + " declaration is: '$parent'.", ); negative2Test( 'Class type parameter from another class', @@ -156,7 +156,7 @@ void main() { }, (Node? node, Node? parent) => "${errorPrefix}Type parameter '$node' referenced out of scope," - " owner is: '$parent'.", + " declaration is: '$parent'.", ); negative2Test( 'Class type parameter in static method', @@ -175,7 +175,7 @@ void main() { }, (Node? node, Node? parent) => "${errorPrefix}Type parameter '$node' referenced from static context," - " parent is: '$parent'.", + " declaration is: '$parent'.", ); negative2Test( 'Class type parameter in static field', @@ -191,7 +191,7 @@ void main() { }, (Node? node, Node? parent) => "${errorPrefix}Type parameter '$node' referenced from static context," - " parent is: '$parent'.", + " declaration is: '$parent'.", ); negative2Test( 'Method type parameter out of scope', @@ -221,7 +221,7 @@ void main() { }, (Node? node, Node? parent) => "${errorPrefix}Type parameter '$node' referenced out of scope," - " owner is: '${(parent as TreeNode).parent}'.", + " declaration is: '${(parent as TreeNode).parent}'.", ); negative1Test( 'Interface type arity too low', @@ -745,7 +745,7 @@ void main() { return foo; }, (Node? foo) => "${errorPrefix}" - "Unset bound on type parameter TypeParameter(T)", + "Unset bound on type parameter TypeParameter(Foo.T)", ); negative1Test( 'Unset default type typedef Foo = dynamic', @@ -759,7 +759,7 @@ void main() { return foo; }, (Node? foo) => "${errorPrefix}" - "Unset default type on type parameter TypeParameter(T)", + "Unset default type on type parameter TypeParameter(Foo.T)", ); negative1Test( 'Non-static top-level field', diff --git a/pkg/vm/lib/transformations/type_flow/summary_collector.dart b/pkg/vm/lib/transformations/type_flow/summary_collector.dart index 5cf0f42a2e5..a80592f77dd 100644 --- a/pkg/vm/lib/transformations/type_flow/summary_collector.dart +++ b/pkg/vm/lib/transformations/type_flow/summary_collector.dart @@ -2657,8 +2657,8 @@ class RuntimeTypeTranslatorImpl extends DartTypeVisitor return result; } } - if (type.parameter.parent is! Class) return unknownType; - final interfaceClass = type.parameter.parent as Class; + final interfaceClass = type.parameter.declaration; + if (interfaceClass is! Class) return unknownType; // Undetermined nullability is equivalent to nonNullable when // instantiating type parameter, so convert it right away. Nullability nullability = type.nullability; diff --git a/pkg/vm/lib/transformations/type_flow/transformer.dart b/pkg/vm/lib/transformations/type_flow/transformer.dart index 8d70c88a1ef..f77561bd4dc 100644 --- a/pkg/vm/lib/transformations/type_flow/transformer.dart +++ b/pkg/vm/lib/transformations/type_flow/transformer.dart @@ -1033,9 +1033,9 @@ class _TreeShakerTypeVisitor extends RecursiveVisitor { @override visitTypeParameterType(TypeParameterType node) { - final parent = node.parameter.parent; - if (parent is Class) { - shaker.addClassUsedInType(parent); + final declaration = node.parameter.declaration; + if (declaration is Class) { + shaker.addClassUsedInType(declaration); } node.visitChildren(this); }