RecordElement.instantiate(), substitution, type alias.

Change-Id: Icd0e234e57d17a1c86140ed58fc9732da31bf1b3
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/254942
Reviewed-by: Samuel Rawlins <srawlins@google.com>
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
This commit is contained in:
Konstantin Shcheglov 2022-08-15 17:31:36 +00:00 committed by Commit Bot
parent 9a3bcbcdb0
commit c66faf9f3a
7 changed files with 154 additions and 22 deletions

View file

@ -2261,6 +2261,11 @@ abstract class RecordElement implements _ExistingElement {
/// The positional fields (might be empty).
List<RecordPositionalFieldElement> get positionalFields;
/// Returns [RecordType] with [nullabilitySuffix] and declared field types.
RecordType instantiate({
required NullabilitySuffix nullabilitySuffix,
});
}
/// A field in a [RecordElement].

View file

@ -178,22 +178,21 @@ class ElementDisplayStringBuilder {
final fieldCount = positionalFields.length + namedFields.length;
_write('(');
for (var i = 0; i < positionalFields.length; i++) {
final field = positionalFields[i];
var index = 0;
for (final field in positionalFields) {
_writeType(field.type);
if (i < fieldCount - 1) {
if (index++ < fieldCount - 1) {
_write(', ');
}
}
if (namedFields.isNotEmpty) {
_write('{');
for (var i = 0; i < namedFields.length; i++) {
final field = namedFields[i];
for (final field in namedFields) {
_writeType(field.type);
_write(' ');
_write(field.name);
if (i < fieldCount - 1) {
if (index++ < fieldCount - 1) {
_write(', ');
}
}

View file

@ -6219,6 +6219,20 @@ class RecordElementImpl extends _ExistingElementImpl implements RecordElement {
throw UnimplementedError();
}
@override
RecordTypeImpl instantiate({
required NullabilitySuffix nullabilitySuffix,
}) {
return RecordTypeImpl(
element2: this,
fieldTypes: [
...positionalFields.map((field) => field.type),
...namedFieldsSorted.map((field) => field.type),
],
nullabilitySuffix: nullabilitySuffix,
);
}
/// Returns [fields], if already sorted, or the sorted copy.
static List<RecordNamedFieldElementImpl> _sortNamedFields(
List<RecordNamedFieldElementImpl> fields,
@ -6597,6 +6611,16 @@ class TypeAliasElementImpl extends _ExistingElementImpl
typeArguments: typeArguments,
),
);
} else if (type is RecordTypeImpl) {
return RecordTypeImpl(
element2: type.element2,
fieldTypes: type.fieldTypes,
nullabilitySuffix: resultNullability,
alias: InstantiatedTypeAliasElementImpl(
element: this,
typeArguments: typeArguments,
),
);
} else if (type is TypeParameterType) {
return TypeParameterTypeImpl(
element: type.element2,

View file

@ -16,6 +16,7 @@ import 'package:analyzer/src/dart/element/type_algebra.dart';
import 'package:analyzer/src/dart/element/type_system.dart';
import 'package:analyzer/src/generated/element_type_provider.dart';
import 'package:analyzer/src/generated/utilities_dart.dart';
import 'package:collection/collection.dart';
/// The [Type] representing the type `dynamic`.
class DynamicTypeImpl extends TypeImpl implements DynamicType {
@ -995,16 +996,18 @@ class RecordTypeImpl extends TypeImpl implements RecordType {
@override
final RecordElementImpl element2;
final Substitution substitution;
/// The types of all fields, first positional, then named.
final List<DartType> fieldTypes;
@override
final NullabilitySuffix nullabilitySuffix;
RecordTypeImpl({
required this.element2,
required this.substitution,
required this.fieldTypes,
required this.nullabilitySuffix,
}) : super(element2);
InstantiatedTypeAliasElement? alias,
}) : super(element2, alias: alias);
@override
RecordElementImpl get element => element2;
@ -1015,23 +1018,22 @@ class RecordTypeImpl extends TypeImpl implements RecordType {
@override
List<RecordTypeNamedField> get namedFields {
return element.namedFieldsSorted.map((field) {
final type = substitution.substituteType(field.type);
final baseIndex = element.positionalFields.length;
return element.namedFieldsSorted.mapIndexed((index, field) {
return RecordTypeNamedFieldImpl(
element: field,
name: field.name,
type: type,
type: fieldTypes[baseIndex + index],
);
}).toList();
}
@override
List<RecordTypePositionalField> get positionalFields {
return element.positionalFields.map((field) {
final type = substitution.substituteType(field.type);
return element.positionalFields.mapIndexed((index, field) {
return RecordTypePositionalFieldImpl(
element: field,
type: type,
type: fieldTypes[index],
);
}).toList();
}

View file

@ -546,9 +546,20 @@ abstract class _TypeSubstitutor
DartType visitNeverType(NeverType type) => type;
@override
DartType visitRecordType(RecordType type) {
// TODO: implement visitRecordType
throw UnimplementedError();
DartType visitRecordType(covariant RecordTypeImpl type) {
final before = useCounter;
final fieldTypes = _mapList(type.fieldTypes);
final alias = _mapAlias(type.alias);
if (useCounter == before) {
return type;
}
return RecordTypeImpl(
element2: type.element2,
fieldTypes: fieldTypes,
nullabilitySuffix: type.nullabilitySuffix,
alias: alias,
);
}
@override

View file

@ -10,7 +10,6 @@ import 'package:analyzer/dart/element/type_provider.dart';
import 'package:analyzer/src/dart/analysis/session.dart';
import 'package:analyzer/src/dart/element/element.dart';
import 'package:analyzer/src/dart/element/type.dart';
import 'package:analyzer/src/dart/element/type_algebra.dart';
import 'package:analyzer/src/dart/element/type_system.dart';
import 'package:analyzer/src/dart/resolver/variance.dart';
import 'package:analyzer/src/generated/engine.dart';
@ -592,10 +591,8 @@ mixin ElementsTypesMixin {
RecordTypeImpl recordTypeNone({
required RecordElementImpl element,
}) {
return RecordTypeImpl(
element2: element,
return element.instantiate(
nullabilitySuffix: NullabilitySuffix.none,
substitution: Substitution.empty,
);
}

View file

@ -377,6 +377,100 @@ class SubstituteTest extends _Base {
_assertIdenticalType(type, {T: intNone});
}
test_record_doesNotUseTypeParameter() async {
final T = typeParameter('T');
final type = recordTypeNone(
element: recordElement(
positionalFields: [
recordPositionalField(type: intNone),
],
),
);
assertType(type, '(int)');
_assertIdenticalType(type, {T: intNone});
}
test_record_fromAlias() async {
// typedef Alias<T> = (int, String);
final T = typeParameter('T');
final Alias = typeAlias(
name: 'Alias',
typeParameters: [T],
aliasedType: recordTypeNone(
element: recordElement(
positionalFields: [
recordPositionalField(type: intNone),
recordPositionalField(type: stringNone),
],
),
),
);
final U = typeParameter('U');
final type = typeAliasTypeNone(Alias, typeArguments: [
typeParameterTypeNone(U),
]);
assertType(type, '(int, String) via Alias<U>');
_assertSubstitution(type, {U: intNone}, '(int, String) via Alias<int>');
}
test_record_fromAlias2() async {
// typedef Alias<T> = (T, List<T>);
final T = typeParameter('T');
final T_none = typeParameterTypeNone(T);
final Alias = typeAlias(
name: 'Alias',
typeParameters: [T],
aliasedType: recordTypeNone(
element: recordElement(
positionalFields: [
recordPositionalField(type: T_none),
recordPositionalField(type: listNone(T_none)),
],
),
),
);
final type = typeAliasTypeNone(Alias, typeArguments: [intNone]);
assertType(type, '(int, List<int>) via Alias<int>');
}
test_record_named() async {
final T = typeParameter('T');
final T_none = typeParameterTypeNone(T);
final type = recordTypeNone(
element: recordElement(
namedFields: [
recordNamedField(name: 'f1', type: T_none),
recordNamedField(name: 'f2', type: listNone(T_none)),
],
),
);
assertType(type, '({T f1, List<T> f2})');
_assertSubstitution(type, {T: intNone}, '({int f1, List<int> f2})');
}
test_record_positional() async {
final T = typeParameter('T');
final T_none = typeParameterTypeNone(T);
final type = recordTypeNone(
element: recordElement(
positionalFields: [
recordPositionalField(type: T_none),
recordPositionalField(type: listNone(T_none)),
],
),
);
assertType(type, '(T, List<T>)');
_assertSubstitution(type, {T: intNone}, '(int, List<int>)');
}
test_typeParameter_nullability() async {
var tElement = typeParameter('T');