From 3f90ef63d9e490d593bef19078990b09903feaa9 Mon Sep 17 00:00:00 2001 From: Johnni Winther Date: Fri, 17 Jan 2025 06:27:21 -0800 Subject: [PATCH] [cfe] Add _ImplicitType to handle field and enum element inference Change-Id: I00bb0817c6504de0f970d58abfaac294c16209e9 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/405003 Reviewed-by: Chloe Stefantsova Commit-Queue: Johnni Winther --- .../lib/src/fragment/enum_element.dart | 49 ++++- pkg/front_end/lib/src/fragment/field.dart | 76 ++++++- pkg/front_end/lib/src/fragment/fragment.dart | 1 + .../lib/src/kernel/implicit_field_type.dart | 208 ++++++------------ .../test/coverage_suite_expected.dart | 10 +- 5 files changed, 180 insertions(+), 164 deletions(-) diff --git a/pkg/front_end/lib/src/fragment/enum_element.dart b/pkg/front_end/lib/src/fragment/enum_element.dart index 9303fa0e4e7d..3f9301cd3251 100644 --- a/pkg/front_end/lib/src/fragment/enum_element.dart +++ b/pkg/front_end/lib/src/fragment/enum_element.dart @@ -20,12 +20,22 @@ class EnumElementFragment final Uri fileUri; final ConstructorReferenceBuilder? constructorReferenceBuilder; - Token? argumentsBeginToken; + Token? _argumentsBeginToken; SourcePropertyBuilder? _builder; Field? _field; - late DartType _type = new InferredType.fromEnumElementInitializer(this); + + late DartType _type = new InferredType( + libraryBuilder: builder.libraryBuilder, + typeBuilder: type, + inferType: inferType, + computeType: _computeType, + fileUri: fileUri, + name: name, + nameOffset: nameOffset, + nameLength: name.length, + token: argumentsBeginToken); late final int elementIndex; @@ -38,7 +48,8 @@ class EnumElementFragment required this.nameOffset, required this.fileUri, required this.constructorReferenceBuilder, - required this.argumentsBeginToken}); + required Token? argumentsBeginToken}) + : _argumentsBeginToken = argumentsBeginToken; @override SourcePropertyBuilder get builder { @@ -53,6 +64,29 @@ class EnumElementFragment type.registerInferredTypeListener(this); } + /// Returns the token for begin of the constructor arguments of this enum + /// element, if any. + /// + /// This can only be called once and will hand over the responsibility of + /// the token to the caller. + Token? get argumentsBeginToken { + Token? token = _argumentsBeginToken; + _argumentsBeginToken = null; + return token; + } + + DartType _computeType(ClassHierarchyBase hierarchy, Token? token) { + SourceLibraryBuilder libraryBuilder = builder.libraryBuilder; + SourceEnumBuilder sourceEnumBuilder = + builder.declarationBuilder as SourceEnumBuilder; + _buildElement( + sourceEnumBuilder, + sourceEnumBuilder.selfType.build(libraryBuilder, TypeUse.enumSelfType), + libraryBuilder.loader.coreTypes, + token); + return fieldType; + } + @override bool get isEnumElement => true; @@ -125,8 +159,8 @@ class EnumElementFragment f(member: _field!, kind: BuiltMemberKind.Field); } - void buildElement(SourceEnumBuilder sourceEnumBuilder, DartType selfType, - CoreTypes coreTypes) { + void _buildElement(SourceEnumBuilder sourceEnumBuilder, DartType selfType, + CoreTypes coreTypes, Token? token) { SourceLibraryBuilder libraryBuilder = sourceEnumBuilder.libraryBuilder; DartType inferredFieldType = selfType; @@ -174,8 +208,8 @@ class EnumElementFragment fileUri); bodyBuilder.constantContext = ConstantContext.inferred; - if (argumentsBeginToken != null) { - arguments = bodyBuilder.parseArguments(argumentsBeginToken!); + if (token != null) { + arguments = bodyBuilder.parseArguments(token); // We pass `true` for [allowFurtherDelays] here because the members of // the enums are built before the inference, and the resolution of the // redirecting factories can't be completed at this moment and @@ -185,7 +219,6 @@ class EnumElementFragment arguments.positional.insertAll(0, enumSyntheticArguments); arguments.argumentsOriginalOrder?.insertAll(0, enumSyntheticArguments); - argumentsBeginToken = null; } else { arguments = new ArgumentsImpl(enumSyntheticArguments); } diff --git a/pkg/front_end/lib/src/fragment/field.dart b/pkg/front_end/lib/src/fragment/field.dart index b4c46ca4c38c..5b9f2a0f0e3d 100644 --- a/pkg/front_end/lib/src/fragment/field.dart +++ b/pkg/front_end/lib/src/fragment/field.dart @@ -66,6 +66,10 @@ class FieldFragment } } + /// Returns the token for the initializer of this field, if any. + /// + /// This can only be called once and will hand over the responsibility of + /// the token to the caller. Token? get initializerToken { Token? result = _initializerToken; // Ensure that we don't hold onto the token. @@ -73,7 +77,12 @@ class FieldFragment return result; } - // Coverage-ignore(suite): Not run. + /// Returns the token for the initializer of this field, if any. This is the + /// same as [initializerToken] but is used to signal that the initializer + /// needs to be computed for outline expressions. + /// + /// This can only be called once and will hand over the responsibility of + /// the token to the caller. Token? get constInitializerToken { Token? result = _constInitializerToken; // Ensure that we don't hold onto the token. @@ -173,13 +182,63 @@ class FieldFragment } else { // A field with no type and initializer or an instance field without // type and initializer need to have the type inferred. - _encoding.type = - new InferredType.fromFieldFragmentInitializer(this, token); + _encoding.type = new InferredType( + libraryBuilder: libraryBuilder, + typeBuilder: type, + inferType: inferType, + computeType: _computeInferredType, + fileUri: fileUri, + name: name, + nameOffset: nameOffset, + nameLength: name.length, + token: token); type.registerInferable(this); } } } + DartType _computeInferredType( + ClassHierarchyBase classHierarchy, Token? token) { + DartType? inferredType; + SourceLibraryBuilder libraryBuilder = builder.libraryBuilder; + DeclarationBuilder? declarationBuilder = builder.declarationBuilder; + if (token != null) { + InterfaceType? enclosingClassThisType = declarationBuilder + is SourceClassBuilder + ? libraryBuilder.loader.typeInferenceEngine.coreTypes + .thisInterfaceType( + declarationBuilder.cls, libraryBuilder.library.nonNullable) + : null; + TypeInferrer typeInferrer = + libraryBuilder.loader.typeInferenceEngine.createTopLevelTypeInferrer( + fileUri, + enclosingClassThisType, + libraryBuilder, + builder + .dataForTesting + // Coverage-ignore(suite): Not run. + ?.inferenceData); + BodyBuilderContext bodyBuilderContext = createBodyBuilderContext(); + BodyBuilder bodyBuilder = libraryBuilder.loader.createBodyBuilderForField( + libraryBuilder, + bodyBuilderContext, + declarationBuilder?.scope ?? libraryBuilder.scope, + typeInferrer, + fileUri); + bodyBuilder.constantContext = + modifiers.isConst ? ConstantContext.inferred : ConstantContext.none; + bodyBuilder.inFieldInitializer = true; + bodyBuilder.inLateFieldInitializer = modifiers.isLate; + Expression initializer = bodyBuilder.parseFieldInitializer(token); + + inferredType = + typeInferrer.inferImplicitFieldType(bodyBuilder, initializer); + } else { + inferredType = const DynamicType(); + } + return inferredType; + } + @override bool get isEnumElement => false; @@ -240,13 +299,13 @@ class FieldFragment // For modular compilation we need to include initializers of all const // fields and all non-static final fields in classes with const constructors // into the outline. + Token? token = constInitializerToken; if ((modifiers.isConst || (isFinal && isClassInstanceMember && (declarationBuilder as SourceClassBuilder) .declaresConstConstructor)) && - _constInitializerToken != null) { - Token initializerToken = _constInitializerToken!; + token != null) { LookupScope scope = declarationBuilder?.scope ?? libraryBuilder.scope; BodyBuilder bodyBuilder = libraryBuilder.loader .createBodyBuilderForOutlineExpression( @@ -255,18 +314,17 @@ class FieldFragment ? ConstantContext.inferred : ConstantContext.required; Expression initializer = bodyBuilder.typeInferrer - .inferFieldInitializer(bodyBuilder, fieldType, - bodyBuilder.parseFieldInitializer(initializerToken)) + .inferFieldInitializer( + bodyBuilder, fieldType, bodyBuilder.parseFieldInitializer(token)) .expression; buildBody(classHierarchy.coreTypes, initializer); bodyBuilder.performBacklogComputations(); if (computeSharedExpressionForTesting) { // Coverage-ignore-block(suite): Not run. _initializerExpression = parseFieldInitializer(libraryBuilder.loader, - initializerToken, libraryBuilder.importUri, fileUri, scope); + token, libraryBuilder.importUri, fileUri, scope); } } - _constInitializerToken = null; } @override diff --git a/pkg/front_end/lib/src/fragment/fragment.dart b/pkg/front_end/lib/src/fragment/fragment.dart index 4eda6be595c5..41515c642050 100644 --- a/pkg/front_end/lib/src/fragment/fragment.dart +++ b/pkg/front_end/lib/src/fragment/fragment.dart @@ -58,6 +58,7 @@ import '../source/source_type_alias_builder.dart'; import '../source/type_parameter_scope_builder.dart'; import '../type_inference/inference_results.dart'; import '../type_inference/type_inference_engine.dart'; +import '../type_inference/type_inferrer.dart'; import '../type_inference/type_schema.dart'; part 'class.dart'; diff --git a/pkg/front_end/lib/src/kernel/implicit_field_type.dart b/pkg/front_end/lib/src/kernel/implicit_field_type.dart index 08f6420fdc2f..c0ccb6a7b706 100644 --- a/pkg/front_end/lib/src/kernel/implicit_field_type.dart +++ b/pkg/front_end/lib/src/kernel/implicit_field_type.dart @@ -10,13 +10,9 @@ import 'package:kernel/src/printer.dart'; import '../base/constant_context.dart'; import '../base/problems.dart' show unsupported; -import '../builder/declaration_builders.dart'; import '../builder/inferable_type_builder.dart'; import '../builder/type_builder.dart'; import '../codes/cfe_codes.dart'; -import '../fragment/fragment.dart'; -import '../source/source_class_builder.dart'; -import '../source/source_enum_builder.dart'; import '../source/source_field_builder.dart'; import '../source/source_library_builder.dart'; import '../type_inference/type_inferrer.dart'; @@ -33,13 +29,16 @@ abstract class InferredType extends AuxiliaryType { SourceFieldBuilder fieldBuilder, Token? initializerToken) = _ImplicitFieldTypeRoot; - factory InferredType.fromFieldFragmentInitializer( - FieldFragment fieldFragment, Token? initializerToken) = - _ImplicitFieldFragmentTypeRoot; - - factory InferredType.fromEnumElementInitializer( - EnumElementFragment enumElementFragment) = - _ImplicitEnumElementFragmentType; + factory InferredType( + {required SourceLibraryBuilder libraryBuilder, + required TypeBuilder typeBuilder, + required InferTypeFunction inferType, + required ComputeTypeFunction computeType, + required Uri fileUri, + required String name, + required int nameOffset, + required int nameLength, + required Token? token}) = _ImplicitType; factory InferredType.fromInferableTypeUse(InferableTypeUse inferableTypeUse) = _InferredTypeUse; @@ -196,174 +195,99 @@ class _ImplicitFieldTypeRoot extends InferredType { String toString() => 'ImplicitFieldType(${toStringInternal()})'; } -class _ImplicitFieldFragmentTypeRoot extends InferredType { - final FieldFragment _fieldFragment; - - Token? initializerToken; - bool isStarted = false; - - _ImplicitFieldFragmentTypeRoot(this._fieldFragment, this.initializerToken) - : super._(); - - @override - // Coverage-ignore(suite): Not run. - Uri get fileUri => _fieldFragment.fileUri; - - @override - // Coverage-ignore(suite): Not run. - int get charOffset => _fieldFragment.nameOffset; - - @override - DartType inferType(ClassHierarchyBase hierarchy) { - return _fieldFragment.inferType(hierarchy); - } - - @override - DartType computeType(ClassHierarchyBase hierarchy) { - if (isStarted) { - _fieldFragment.builder.libraryBuilder.addProblem( - templateCantInferTypeDueToCircularity - .withArguments(_fieldFragment.name), - _fieldFragment.nameOffset, - _fieldFragment.name.length, - _fieldFragment.fileUri); - DartType type = const InvalidType(); - _fieldFragment.type.registerInferredType(type); - return type; - } - isStarted = true; - DartType? inferredType; - SourceLibraryBuilder libraryBuilder = _fieldFragment.builder.libraryBuilder; - DeclarationBuilder? declarationBuilder = - _fieldFragment.builder.declarationBuilder; - if (initializerToken != null) { - InterfaceType? enclosingClassThisType = declarationBuilder - is SourceClassBuilder - ? libraryBuilder.loader.typeInferenceEngine.coreTypes - .thisInterfaceType( - declarationBuilder.cls, libraryBuilder.library.nonNullable) - : null; - TypeInferrer typeInferrer = - libraryBuilder.loader.typeInferenceEngine.createTopLevelTypeInferrer( - _fieldFragment.fileUri, - enclosingClassThisType, - libraryBuilder, - _fieldFragment - .builder - .dataForTesting - // Coverage-ignore(suite): Not run. - ?.inferenceData); - BodyBuilderContext bodyBuilderContext = - _fieldFragment.createBodyBuilderContext(); - BodyBuilder bodyBuilder = libraryBuilder.loader.createBodyBuilderForField( - libraryBuilder, - bodyBuilderContext, - declarationBuilder?.scope ?? libraryBuilder.scope, - typeInferrer, - _fieldFragment.fileUri); - bodyBuilder.constantContext = _fieldFragment.modifiers.isConst - ? ConstantContext.inferred - : ConstantContext.none; - bodyBuilder.inFieldInitializer = true; - bodyBuilder.inLateFieldInitializer = _fieldFragment.modifiers.isLate; - Expression initializer = - bodyBuilder.parseFieldInitializer(initializerToken!); - initializerToken = null; - - inferredType = - typeInferrer.inferImplicitFieldType(bodyBuilder, initializer); - } else { - inferredType = const DynamicType(); - } - return inferredType; - } - - @override - // Coverage-ignore(suite): Not run. - void toTextInternal(AstPrinter printer) { - printer.write(''); - } - - @override - // Coverage-ignore(suite): Not run. - bool equals(Object other, Assumptions? assumptions) { - if (identical(this, other)) return true; - return other is _ImplicitFieldFragmentTypeRoot && - _fieldFragment == other._fieldFragment; - } - - @override - int get hashCode => _fieldFragment.hashCode; - - @override - String toString() => 'ImplicitFieldType(${toStringInternal()})'; -} - -class _ImplicitEnumElementFragmentType extends InferredType { - final EnumElementFragment _enumElementFragment; +/// Signature for function called to trigger the inference of the type of +/// [_ImplicitType], if it hasn't already been computed. +typedef InferTypeFunction = DartType Function(ClassHierarchyBase hierarchy); + +/// Signature for function called to compute the type for [_ImplicitType] +typedef ComputeTypeFunction = DartType Function( + ClassHierarchyBase hierarchy, Token? token); + +/// [InferredType] implementation that infers the type of [_typeBuilder] using +/// [_computeType] and [_token]. +class _ImplicitType extends InferredType { + final SourceLibraryBuilder _libraryBuilder; + final TypeBuilder _typeBuilder; + final InferTypeFunction _inferType; + final ComputeTypeFunction _computeType; + final Uri _fileUri; + final String _name; + final int _nameOffset; + final int _nameLength; + Token? _token; bool isStarted = false; - _ImplicitEnumElementFragmentType(this._enumElementFragment) : super._(); + _ImplicitType( + {required SourceLibraryBuilder libraryBuilder, + required TypeBuilder typeBuilder, + required InferTypeFunction inferType, + required ComputeTypeFunction computeType, + required Uri fileUri, + required String name, + required int nameOffset, + required int nameLength, + required Token? token}) + : _libraryBuilder = libraryBuilder, + _typeBuilder = typeBuilder, + _inferType = inferType, + _computeType = computeType, + _fileUri = fileUri, + _name = name, + _nameOffset = nameOffset, + _nameLength = nameLength, + _token = token, + super._(); @override // Coverage-ignore(suite): Not run. - Uri get fileUri => _enumElementFragment.fileUri; + Uri get fileUri => _fileUri; @override // Coverage-ignore(suite): Not run. - int get charOffset => _enumElementFragment.nameOffset; + int get charOffset => _nameOffset; @override DartType inferType(ClassHierarchyBase hierarchy) { - return _enumElementFragment.inferType(hierarchy); + return _inferType(hierarchy); } @override DartType computeType(ClassHierarchyBase hierarchy) { if (isStarted) { - // Coverage-ignore-block(suite): Not run. - _enumElementFragment.builder.libraryBuilder.addProblem( - templateCantInferTypeDueToCircularity - .withArguments(_enumElementFragment.name), - _enumElementFragment.nameOffset, - _enumElementFragment.name.length, - _enumElementFragment.fileUri); + _libraryBuilder.addProblem( + templateCantInferTypeDueToCircularity.withArguments(_name), + _nameOffset, + _nameLength, + _fileUri); DartType type = const InvalidType(); - _enumElementFragment.type.registerInferredType(type); + _typeBuilder.registerInferredType(type); return type; } isStarted = true; - SourceLibraryBuilder libraryBuilder = - _enumElementFragment.builder.libraryBuilder; - SourceEnumBuilder sourceEnumBuilder = - _enumElementFragment.builder.declarationBuilder as SourceEnumBuilder; - _enumElementFragment.buildElement( - sourceEnumBuilder, - sourceEnumBuilder.selfType.build(libraryBuilder, TypeUse.enumSelfType), - libraryBuilder.loader.coreTypes); - return _enumElementFragment.fieldType; + Token? token = _token; + _token = null; + return _computeType(hierarchy, token); } @override // Coverage-ignore(suite): Not run. void toTextInternal(AstPrinter printer) { - printer.write(''); + printer.write(''); } @override // Coverage-ignore(suite): Not run. bool equals(Object other, Assumptions? assumptions) { if (identical(this, other)) return true; - return other is _ImplicitEnumElementFragmentType && - _enumElementFragment == other._enumElementFragment; + return other is _ImplicitType && _typeBuilder == other._typeBuilder; } @override - int get hashCode => _enumElementFragment.hashCode; + int get hashCode => _typeBuilder.hashCode; @override - String toString() => 'ImplicitFieldType(${toStringInternal()})'; + String toString() => '_ImplicitType(${toStringInternal()})'; } class _InferredTypeUse extends InferredType { diff --git a/pkg/front_end/test/coverage_suite_expected.dart b/pkg/front_end/test/coverage_suite_expected.dart index b5b6fc4b564a..7962768766f5 100644 --- a/pkg/front_end/test/coverage_suite_expected.dart +++ b/pkg/front_end/test/coverage_suite_expected.dart @@ -490,7 +490,7 @@ const Map _expect = { ), // 100.0%. "package:front_end/src/fragment/enum_element.dart": ( - hitCount: 224, + hitCount: 242, missCount: 0, ), // 100.0%. @@ -510,7 +510,7 @@ const Map _expect = { ), // 100.0%. "package:front_end/src/fragment/field.dart": ( - hitCount: 259, + hitCount: 302, missCount: 0, ), // 100.0%. @@ -700,7 +700,7 @@ const Map _expect = { ), // 100.0%. "package:front_end/src/kernel/implicit_field_type.dart": ( - hitCount: 93, + hitCount: 25, missCount: 0, ), // 100.0%. @@ -925,7 +925,7 @@ const Map _expect = { ), // 100.0%. "package:front_end/src/source/source_enum_builder.dart": ( - hitCount: 335, + hitCount: 331, missCount: 0, ), // 100.0%. @@ -961,7 +961,7 @@ const Map _expect = { ), // 100.0%. "package:front_end/src/source/source_loader.dart": ( - hitCount: 1868, + hitCount: 1855, missCount: 0, ), // 100.0%.