Skip to content

Commit 86500f1

Browse files
scheglovCommit Queue
authored andcommitted
Elements. Compute and serialize hasNonFinalField for interfaces.
Compute `InterfaceElementImpl.hasNonFinalField` once during linking and persist it in summaries for classes and mixins. This replaces the previous on-demand, tracked getter with a stored boolean initialized via a recursive walk over the superclass and mixin chain. Key changes: - Add `bool hasNonFinalField` to `InterfaceElementImpl`. - Compute the value in `link.dart::_computeHasNonFinalField()` after `_resolveTypes()` and before `_setDefaultSupertypes()`, memoizing to avoid recomputation. - Serialize/deserialize the value in bundle writer/reader for class and mixin elements. - Remove the expensive getter and its requirement recording. - Bump `DATA_VERSION` to 525. Why: - Avoid repeatedly traversing hierarchies at query time. - Make the property available across library boundaries via summaries. - Reduce analysis overhead for checks that depend on field mutability (e.g., const constructor eligibility). Impact: - No intended behavioral change; the result matches the prior logic: true if any instance, non-const, non-static, non-synthetic field is declared in the class, its supertypes, or applied mixins. - Older summaries are invalidated by the version bump. Change-Id: I2db038341d5a1d9c2d039bc73db9b6bd1e566056 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/446002 Reviewed-by: Johnni Winther <[email protected]> Commit-Queue: Konstantin Shcheglov <[email protected]>
1 parent ad27629 commit 86500f1

File tree

6 files changed

+47
-43
lines changed

6 files changed

+47
-43
lines changed

pkg/analyzer/lib/src/dart/analysis/driver.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ testFineAfterLibraryAnalyzerHook;
109109
// TODO(scheglov): Clean up the list of implicitly analyzed files.
110110
class AnalysisDriver {
111111
/// The version of data format, should be incremented on every format change.
112-
static const int DATA_VERSION = 524;
112+
static const int DATA_VERSION = 525;
113113

114114
/// The number of exception contexts allowed to write. Once this field is
115115
/// zero, we stop writing any new exception contexts in this process.

pkg/analyzer/lib/src/dart/element/element.dart

Lines changed: 3 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -4556,6 +4556,9 @@ abstract class InterfaceElementImpl extends InstanceElementImpl
45564556
/// of this class have been inferred.
45574557
bool hasBeenInferred = false;
45584558

4559+
/// Whether the class or its superclass declares a non-final instance field.
4560+
bool hasNonFinalField = false;
4561+
45594562
@override
45604563
List<InterfaceTypeImpl> get allSupertypes {
45614564
return _allSupertypes ??= library.session.classHierarchy
@@ -4612,42 +4615,6 @@ abstract class InterfaceElementImpl extends InstanceElementImpl
46124615
];
46134616
}
46144617

4615-
@trackedDirectlyExpensive
4616-
bool get hasNonFinalField {
4617-
globalResultRequirements?.record_interfaceElement_hasNonFinalField(
4618-
element: this,
4619-
);
4620-
4621-
var classesToVisit = <InterfaceElementImpl>[];
4622-
var visitedClasses = <InterfaceElementImpl>{};
4623-
classesToVisit.add(this);
4624-
while (classesToVisit.isNotEmpty) {
4625-
var currentElement = classesToVisit.removeAt(0);
4626-
if (visitedClasses.add(currentElement)) {
4627-
// check fields
4628-
for (var field in currentElement.fields) {
4629-
if (!field.isFinal &&
4630-
!field.isConst &&
4631-
!field.isStatic &&
4632-
!field.isSynthetic) {
4633-
return true;
4634-
}
4635-
}
4636-
// check mixins
4637-
for (var mixinType in currentElement.mixins) {
4638-
classesToVisit.add(mixinType.element);
4639-
}
4640-
// check super
4641-
var supertype = currentElement.supertype;
4642-
if (supertype != null) {
4643-
classesToVisit.add(supertype.element);
4644-
}
4645-
}
4646-
}
4647-
// not found
4648-
return false;
4649-
}
4650-
46514618
InheritanceManager3 get inheritanceManager {
46524619
return library.session.inheritanceManager;
46534620
}

pkg/analyzer/lib/src/fine/requirements.dart

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1065,12 +1065,6 @@ class RequirementsManifest {
10651065
requirements.requestedConstructors[constructorName] = constructorId;
10661066
}
10671067

1068-
void record_interfaceElement_hasNonFinalField({
1069-
required InterfaceElementImpl element,
1070-
}) {
1071-
// TODO(scheglov): implement.
1072-
}
1073-
10741068
/// Record that all accessible extensions inside a [LibraryFragmentImpl]
10751069
/// are requested, which means dependency on all extensions exported
10761070
/// from [importedLibraries].

pkg/analyzer/lib/src/summary2/bundle_reader.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,7 @@ class LibraryReader {
266266
var element = ClassElementImpl(reference, fragments.first);
267267
element.linkFragments(fragments);
268268
element.readModifiers(_reader);
269+
element.hasNonFinalField = _reader.readBool();
269270

270271
// Configure for reading members lazily.
271272
_lazyRead((offset) {
@@ -953,6 +954,7 @@ class LibraryReader {
953954
var element = MixinElementImpl(reference, fragments.first);
954955
element.linkFragments(fragments);
955956
element.readModifiers(_reader);
957+
element.hasNonFinalField = _reader.readBool();
956958

957959
// TODO(scheglov): consider reading lazily
958960
for (var fragment in element.fragments) {

pkg/analyzer/lib/src/summary2/bundle_writer.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ class BundleWriter {
140140
_writeReference(element.reference);
141141
_writeFragments(element.fragments);
142142
element.writeModifiers(_sink);
143+
_sink.writeBool(element.hasNonFinalField);
143144

144145
// We read members lazily.
145146
_writeForLazyRead(() {
@@ -561,6 +562,7 @@ class BundleWriter {
561562
_writeReference(element.reference);
562563
_writeFragments(element.fragments);
563564
element.writeModifiers(_sink);
565+
_sink.writeBool(element.hasNonFinalField);
564566

565567
// TODO(scheglov): consider reading lazily
566568
_resolutionSink.withTypeParameters(element.typeParameters, () {

pkg/analyzer/lib/src/summary2/link.dart

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,7 @@ class Linker {
232232

233233
_createTypeSystem();
234234
_resolveTypes();
235+
_computeHasNonFinalField();
235236
_setDefaultSupertypes();
236237

237238
_buildClassSyntheticConstructors();
@@ -265,6 +266,44 @@ class Linker {
265266
}
266267
}
267268

269+
/// Set [InterfaceElementImpl.hasNonFinalField] for classes and mixins.
270+
///
271+
/// We actually use it only for classes (which can have `const` constructors),
272+
/// but mixins contribute to it.
273+
void _computeHasNonFinalField() {
274+
var linkingElements =
275+
builders.values
276+
.expand((builder) => builder.element.children)
277+
.whereType<InterfaceElementImpl>()
278+
.toSet();
279+
280+
var alreadyComputed = Set<InterfaceElementImpl>.identity();
281+
282+
bool computeFor(InterfaceElementImpl element) {
283+
if (!linkingElements.contains(element) || !alreadyComputed.add(element)) {
284+
return element.hasNonFinalField;
285+
}
286+
287+
var hasNonFinalField = [
288+
element.supertype,
289+
...element.mixins,
290+
].nonNulls.any((type) => computeFor(type.element));
291+
292+
hasNonFinalField |= element.fields.any((field) {
293+
return !field.isFinal &&
294+
!field.isConst &&
295+
!field.isStatic &&
296+
!field.isSynthetic;
297+
});
298+
299+
return element.hasNonFinalField = hasNonFinalField;
300+
}
301+
302+
for (var element in linkingElements) {
303+
computeFor(element);
304+
}
305+
}
306+
268307
void _computeLibraryScopes({required OperationPerformanceImpl performance}) {
269308
globalResultRequirements.untracked(
270309
reason: 'No complete elements yet',

0 commit comments

Comments
 (0)