[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 <fishythefish@google.com>
Reviewed-by: Jens Johansen <jensj@google.com>
Commit-Queue: Johnni Winther <johnniwinther@google.com>
Reviewed-by: Aske Simon Christensen <askesc@google.com>
Reviewed-by: Alexander Markov <alexmarkov@google.com>
This commit is contained in:
Johnni Winther 2023-09-08 08:32:25 +00:00 committed by Commit Queue
parent 5b44717c63
commit e401b6f18a
30 changed files with 320 additions and 183 deletions

View file

@ -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<T>(int)`.
kind = TypeVariableKind.function;
} else if (typeDeclaration is ir.Class) {
// We have a class type variable, like `T` in `class Class<T> { ... }`.
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<T>() { ... }`.
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<T>() { ... } ... }`.
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<T>() { ... }`.
kind = TypeVariableKind.method;
context = typeDeclaration;
}
} else {
// We have a generic local function type variable, like `T` in
// `m() { local<T>() { ... } ... }`.
assert(typeDeclaration is ir.LocalFunction,
"Unexpected type declaration: $typeDeclaration");
kind = TypeVariableKind.local;
context = typeDeclaration;
}
return TypeVariableTypeWithContext.internal(
type, context, kind, typeDeclaration);

View file

@ -416,10 +416,10 @@ class ScopeModelBuilder extends ir.Visitor<EvaluationComplexity>
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) {

View file

@ -103,7 +103,7 @@ class DartTypeConverter extends ir.DartTypeVisitor<DartType> {
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();

View file

@ -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;
}

View file

@ -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.');
}

View file

@ -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}");
}
}

View file

@ -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);

View file

@ -3057,7 +3057,8 @@ class CodeGenerator extends ExpressionVisitor1<w.ValueType, w.ValueType>
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<w.ValueType, w.ValueType>
}
} 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);

View file

@ -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 ||

View file

@ -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;
}

View file

@ -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 =

View file

@ -159,7 +159,7 @@ Component createExpressionEvaluationComponent(Procedure procedure) {
Map<TypeParameter, DartType> typeSubstitution = <TypeParameter, DartType>{};
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);

View file

@ -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;

View file

@ -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);
}

View file

@ -4,7 +4,10 @@
/*library: nnbd=true*/
T id<T>(T /*T%*/ t) => t;
T id<T>(
T /*normal|limited.id.T%*/ /*verbose.library org-dartlang-test:///a/b/c/main.dart::id.T%*/
t) =>
t;
class Class1<T> {
const Class1();

View file

@ -7,7 +7,7 @@
/*library: nnbd=false*/
library test;
T id<T>(T /*T**/ t) => t;
T id<T>(T /*normal|limited.id.T**/ /*verbose.test::id.T**/ t) => t;
class Class1<T> {
const Class1();

View file

@ -70,8 +70,16 @@ get topLevelGetter => 42;
set topLevelSetter(/*dynamic*/ _) {}
topLevelMethod() {}
T genericTopLevelMethod1<T>(T /*T%*/ t) => t;
T genericTopLevelMethod2<T, S>(T /*T%*/ t, S /*S%*/ s) => t;
T genericTopLevelMethod1<T>(
T /*normal|limited.genericTopLevelMethod1.T%*/ /*verbose.expressions::genericTopLevelMethod1.T%*/
t) =>
t;
T genericTopLevelMethod2<T, S>(
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;

View file

@ -71,14 +71,44 @@ futureOrType(
/*normal|limited.FutureOr<int?>?*/
/*verbose.FutureOr<dart.core::int?>?*/
o4) {}
typeVariableType1<T>(T /*T%*/ o1, T? /*T?*/ o2) {}
typeVariableType2<T extends num>(T /*T*/ o1, T? /*T?*/ o2) {}
typeVariableType1<T>(
T /*normal|limited.typeVariableType1.T%*/ /*verbose.test::typeVariableType1.T%*/
o1,
T? /*normal|limited.typeVariableType1.T?*/ /*verbose.test::typeVariableType1.T?*/
o2) {}
typeVariableType2<T extends num>(
T /*normal|limited.typeVariableType2.T*/ /*verbose.test::typeVariableType2.T*/
o1,
T? /*normal|limited.typeVariableType2.T?*/ /*verbose.test::typeVariableType2.T?*/
o2) {}
typeVariableType3<T extends S, S>(
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, S extends T>(
T /*T%*/ o1, S /*S%*/ p1, T? /*T?*/ o2, S? /*S?*/ p2) {}
typeVariableType5<T extends Object>(T /*T*/ o1, T? /*T?*/ o2) {}
typeVariableType6<T extends Object?>(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 extends Object>(
T /*normal|limited.typeVariableType5.T*/ /*verbose.test::typeVariableType5.T*/
o1,
T? /*normal|limited.typeVariableType5.T?*/ /*verbose.test::typeVariableType5.T?*/
o2) {}
typeVariableType6<T extends Object?>(
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(

View file

@ -42,11 +42,25 @@ futureOrType(
/*normal|limited.FutureOr<int*>**/
/*verbose.FutureOr<dart.core::int*>**/
o) {}
typeVariableType1<T>(T /*T**/ o) {}
typeVariableType2<T extends num>(T /*T**/ o) {}
typeVariableType3<T extends S, S>(T /*T**/ o, S /*S**/ p) {}
typeVariableType4<T, S extends T>(T /*T**/ o, S /*S**/ p) {}
typeVariableType5<T extends Object>(T /*T**/ o) {}
typeVariableType1<T>(
T /*normal|limited.typeVariableType1.T**/ /*verbose.test::typeVariableType1.T**/
o) {}
typeVariableType2<T extends num>(
T /*normal|limited.typeVariableType2.T**/ /*verbose.test::typeVariableType2.T**/
o) {}
typeVariableType3<T extends S, S>(
T /*normal|limited.typeVariableType3.T**/ /*verbose.test::typeVariableType3.T**/
o,
S /*normal|limited.typeVariableType3.S**/ /*verbose.test::typeVariableType3.S**/
p) {}
typeVariableType4<T, S extends T>(
T /*normal|limited.typeVariableType4.T**/ /*verbose.test::typeVariableType4.T**/
o,
S /*normal|limited.typeVariableType4.S**/ /*verbose.test::typeVariableType4.S**/
p) {}
typeVariableType5<T extends Object>(
T /*normal|limited.typeVariableType5.T**/ /*verbose.test::typeVariableType5.T**/
o) {}
functionType1(void Function() /*void Function()**/ o) {}
functionType2(
int Function(int)

View file

@ -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<Expression> annotations = const <Expression>[];
String name;
@override
final List<TypeParameter> typeParameters;
// TODO(johnniwinther): Make this non-nullable.
DartType? type;
@ -974,13 +978,26 @@ class DirtifyingList<E> extends ListBase<E> {
}
}
/// Declaration that can introduce [TypeParameter]s.
sealed class GenericDeclaration implements TreeNode {
/// The type parameters introduced by this declaration.
List<TypeParameter> 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<TypeParameter> 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<TypeParameter> 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<TypeParameter> 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<TypeParameter> 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<TypeParameter> 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<TypeParameter> get typeParameters => function.typeParameters;
@override
R accept<R>(StatementVisitor<R> 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 {

View file

@ -3936,7 +3936,7 @@ class BinaryBuilder {
}
List<TypeParameter> readAndPushTypeParameterList(
[List<TypeParameter>? list, TreeNode? parent]) {
[List<TypeParameter>? 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<TypeParameter>.generate(
length, (_) => new TypeParameter(null, null)..parent = parent,
list = new List<TypeParameter>.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);

View file

@ -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) {

View file

@ -11,7 +11,7 @@ import '../ast.dart';
import '../import_table.dart';
import '../src/text_util.dart';
abstract class Namer<T> {
abstract mixin class Namer<T> {
int index = 0;
final Map<T, String> map = <T, String>{};
@ -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<void> 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.
}
}

View file

@ -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<N> implements Indentation {
abstract mixin class Tagging<N> implements Indentation {
StringBuffer sb = new StringBuffer();
List<String> tagStack = [];

View file

@ -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<bool> {
@override
bool visitTypeParameterType(TypeParameterType node) {
return node.parameter.parent == null && !variables.contains(node.parameter);
return node.parameter.declaration == null &&
!variables.contains(node.parameter);
}
@override

View file

@ -747,7 +747,7 @@ class VerifyingVisitor extends RecursiveResultVisitor<void> {
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<void> {
@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}'.");
}
}

View file

@ -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:

View file

@ -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<T> = 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',

View file

@ -2657,8 +2657,8 @@ class RuntimeTypeTranslatorImpl extends DartTypeVisitor<TypeExpr>
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;

View file

@ -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);
}