Skip to content

Commit 83af090

Browse files
stereotype441Commit Queue
authored and
Commit Queue
committed
Front end: Fix field promotion for extension type representation variable.
Update the type inference algorithm so that `FlowAnalysis.propertyGet` is appropriately called when analyzing code that refers to the representation variable of an extension type. Flow analysis requires an arbitrary `Object` to represent the property in question; this is later passed to `OperationsCfe.isPropertyPromotable`. We use the synthetic "representation field" (which is actually a `Procedure`) for this purpose (`ExtensionTypeRepresentationAccessTarget.representationField`). Representation variables are considered promotable if they are public (see dart-lang/language#3411). `OperationsCfe.isPropertyPromotable` can tell if the representation variable is public by checking the name of the synthetic representation field. Fixes #53439. Bug: #53439 Change-Id: I8f7ff8fcd8e5a43de419b8441951b52a555fda1a Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/332480 Commit-Queue: Paul Berry <[email protected]> Reviewed-by: Chloe Stefantsova <[email protected]>
1 parent 24936fe commit 83af090

13 files changed

+449
-1
lines changed

pkg/front_end/lib/src/fasta/source/source_library_builder.dart

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1682,6 +1682,13 @@ class SourceLibraryBuilder extends LibraryBuilderImpl {
16821682
while (extensionTypeIterator.moveNext()) {
16831683
SourceExtensionTypeDeclarationBuilder extensionType =
16841684
extensionTypeIterator.current;
1685+
Member? representationGetter =
1686+
extensionType.representationFieldBuilder?.readTarget;
1687+
if (representationGetter != null &&
1688+
!representationGetter.name.isPrivate) {
1689+
individualPropertyReasons[representationGetter] =
1690+
PropertyNonPromotabilityReason.isNotPrivate;
1691+
}
16851692
for (Builder member in extensionType.scope.localMembers) {
16861693
if (member is SourceProcedureBuilder &&
16871694
!member.isStatic &&

pkg/front_end/lib/src/fasta/type_inference/inference_visitor.dart

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6690,7 +6690,9 @@ class InferenceVisitorImpl extends InferenceVisitorBase
66906690
propertyGetNode,
66916691
computePropertyTarget(receiver),
66926692
propertyName.text,
6693-
readTarget.member,
6693+
readTarget is ExtensionTypeRepresentationAccessTarget
6694+
? readTarget.representationField
6695+
: readTarget.member,
66946696
readType);
66956697
return createPropertyGet(
66966698
fileOffset: fileOffset,

pkg/front_end/lib/src/fasta/type_inference/inference_visitor_base.dart

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3401,6 +3401,14 @@ abstract class InferenceVisitorBase implements InferenceVisitor {
34013401
case ObjectAccessTargetKind.extensionTypeRepresentation:
34023402
case ObjectAccessTargetKind.nullableExtensionTypeRepresentation:
34033403
DartType type = target.getGetterType(this);
3404+
type = flowAnalysis.propertyGet(
3405+
null,
3406+
computePropertyTarget(receiver),
3407+
name.text,
3408+
(target as ExtensionTypeRepresentationAccessTarget)
3409+
.representationField,
3410+
type) ??
3411+
type;
34043412
Expression read = new AsExpression(receiver, type)
34053413
..isForNonNullableByDefault = true
34063414
..isUnchecked = true

pkg/front_end/lib/src/fasta/type_inference/type_inference_engine.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -525,6 +525,10 @@ class OperationsCfe
525525
return false;
526526
}
527527
if (property is Procedure) {
528+
if (property.stubKind == ProcedureStubKind.RepresentationField) {
529+
// Representation fields are promotable if they're non-public.
530+
return property.name.isPrivate;
531+
}
528532
if (!property.isAccessor) {
529533
// We don't promote methods.
530534
return false;
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
// Tests that field promotion of the representation variable works properly in
6+
// cascade expressions.
7+
//
8+
// These tests specifically exercise extension types whose representation type
9+
// is nullable, since those are handled differently by the front end than
10+
// extension types whose representation type is non-nullable.
11+
12+
// SharedOptions=--enable-experiment=inline-class
13+
14+
import '../static_type_helper.dart';
15+
16+
extension type E(Object Function()? _f) {
17+
testExplicitThisAccess() {
18+
if (this._f != null) {
19+
this.._f.expectStaticType<Exactly<Object Function()>>();
20+
this.._f().expectStaticType<Exactly<Object>>();
21+
}
22+
}
23+
}
24+
25+
testGeneralPropertyAccess(E e) {
26+
if (e._f != null) {
27+
e.._f.expectStaticType<Exactly<Object Function()>>();
28+
e.._f().expectStaticType<Exactly<Object>>();
29+
}
30+
}
31+
32+
main() {
33+
int Function() f = () => 0;
34+
E(f).testExplicitThisAccess();
35+
testGeneralPropertyAccess(E(f));
36+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
// Tests that field promotion of the representation variable works properly in
6+
// cascade expressions.
7+
8+
// SharedOptions=--enable-experiment=inline-class
9+
10+
import '../static_type_helper.dart';
11+
12+
extension type E(Object Function() _f) {
13+
testExplicitThisAccess() {
14+
if (this._f is int Function()) {
15+
this.._f.expectStaticType<Exactly<int Function()>>();
16+
this.._f().expectStaticType<Exactly<int>>();
17+
}
18+
}
19+
}
20+
21+
testGeneralPropertyAccess(E e) {
22+
if (e._f is int Function()) {
23+
e.._f.expectStaticType<Exactly<int Function()>>();
24+
e.._f().expectStaticType<Exactly<int>>();
25+
}
26+
}
27+
28+
main() {
29+
int Function() f = () => 0;
30+
E(f).testExplicitThisAccess();
31+
testGeneralPropertyAccess(E(f));
32+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
// Tests that if the representation variable has the same name as other
6+
// non-promotable things in the file, it may nonetheless undergo field promotion
7+
// (this is sound because extension types are resolved statically, so there is
8+
// no danger of the representation variable aliasing to some other declaration,
9+
// as there is for ordinary fields).
10+
11+
// SharedOptions=--enable-experiment=inline-class
12+
13+
import '../static_type_helper.dart';
14+
15+
class C1 {
16+
int? _f = 0;
17+
}
18+
19+
class C2 {
20+
int? get _f => 0;
21+
}
22+
23+
class C3 implements C2 {
24+
noSuchMethod(invocation) => 0;
25+
}
26+
27+
extension type E(Object Function() _f) {
28+
testImplicitThisAccess() {
29+
if (_f is int Function()) {
30+
_f.expectStaticType<Exactly<int Function()>>();
31+
_f().expectStaticType<Exactly<int>>();
32+
}
33+
}
34+
35+
testExplicitThisAccess() {
36+
if (this._f is int Function()) {
37+
this._f.expectStaticType<Exactly<int Function()>>();
38+
this._f().expectStaticType<Exactly<int>>();
39+
}
40+
}
41+
}
42+
43+
testGeneralPropertyAccess(E e) {
44+
if ((e)._f is int Function()) {
45+
(e)._f.expectStaticType<Exactly<int Function()>>();
46+
(e)._f().expectStaticType<Exactly<int>>();
47+
}
48+
}
49+
50+
testPrefixedIdentifierAccess(E e) {
51+
// Note: the analyzer has a special representation for property accesses of
52+
// the form `IDENTIFIER.IDENTIFIER`, so we test this form separately.
53+
if (e._f is int Function()) {
54+
e._f.expectStaticType<Exactly<int Function()>>();
55+
e._f().expectStaticType<Exactly<int>>();
56+
}
57+
}
58+
59+
main() {
60+
int Function() f = () => 0;
61+
E(f).testImplicitThisAccess();
62+
E(f).testExplicitThisAccess();
63+
testGeneralPropertyAccess(E(f));
64+
testPrefixedIdentifierAccess(E(f));
65+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
// Tests that flow analysis treats representation variables as independent of
6+
// extension type objects. That is, if `e` is a promotable expression whose type
7+
// is an extension type, and its representation variable is `_f`, null checks
8+
// and `is` tests applied to `e` should not affect the type of `e._f`.
9+
//
10+
// SharedOptions=--enable-experiment=inline-class
11+
12+
import '../static_type_helper.dart';
13+
14+
extension type E(Object Function() _f) {
15+
testImplicitThisAccess() {
16+
if (this is int Function()) {
17+
_f.expectStaticType<Exactly<Object Function()>>();
18+
_f().expectStaticType<Exactly<Object>>();
19+
}
20+
}
21+
22+
testExplicitThisAccess() {
23+
if (this._f is Object Function()) {
24+
this._f.expectStaticType<Exactly<Object Function()>>();
25+
this._f().expectStaticType<Exactly<Object>>();
26+
}
27+
}
28+
}
29+
30+
testGeneralPropertyAccess(E e) {
31+
if (e is int Function()) {
32+
(e)._f.expectStaticType<Exactly<Object Function()>>();
33+
(e)._f().expectStaticType<Exactly<Object>>();
34+
}
35+
}
36+
37+
testPrefixedIdentifierAccess(E e) {
38+
// Note: the analyzer has a special representation for property accesses of
39+
// the form `IDENTIFIER.IDENTIFIER`, so we test this form separately.
40+
if (e is int Function()) {
41+
e._f.expectStaticType<Exactly<Object Function()>>();
42+
e._f().expectStaticType<Exactly<Object>>();
43+
}
44+
}
45+
46+
main() {
47+
int Function() f = () => 0;
48+
E(f).testImplicitThisAccess();
49+
E(f).testExplicitThisAccess();
50+
testGeneralPropertyAccess(E(f));
51+
testPrefixedIdentifierAccess(E(f));
52+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
// Tests that the presence of a representation variable with a given name
6+
// doesn't interfere with promotability of fields having the same name elsewhere
7+
// in the library.
8+
9+
// SharedOptions=--enable-experiment=inline-class
10+
11+
import '../static_type_helper.dart';
12+
13+
extension type E(int? _field) {}
14+
15+
class C {
16+
final int? _field;
17+
C(this._field);
18+
}
19+
20+
test(C c) {
21+
if (c._field != null) {
22+
c._field.expectStaticType<Exactly<int>>();
23+
}
24+
}
25+
26+
main() {
27+
test(C(0));
28+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
// Tests that the representation variable may undergo field promotion, assuming
6+
// that it's private.
7+
//
8+
// These tests specifically exercise extension types whose representation type
9+
// is nullable, since those are handled differently by the front end than
10+
// extension types whose representation type is non-nullable.
11+
12+
// SharedOptions=--enable-experiment=inline-class
13+
14+
import '../static_type_helper.dart';
15+
16+
extension type E(Object Function()? _f) {
17+
testImplicitThisAccess() {
18+
if (_f != null) {
19+
_f.expectStaticType<Exactly<Object Function()>>();
20+
_f().expectStaticType<Exactly<Object>>();
21+
}
22+
}
23+
24+
testExplicitThisAccess() {
25+
if (this._f != null) {
26+
this._f.expectStaticType<Exactly<Object Function()>>();
27+
this._f().expectStaticType<Exactly<Object>>();
28+
}
29+
}
30+
}
31+
32+
testGeneralPropertyAccess(E e) {
33+
if ((e)._f != null) {
34+
(e)._f.expectStaticType<Exactly<Object Function()>>();
35+
(e)._f().expectStaticType<Exactly<Object>>();
36+
}
37+
}
38+
39+
testPrefixedIdentifierAccess(E e) {
40+
// Note: the analyzer has a special representation for property accesses of
41+
// the form `IDENTIFIER.IDENTIFIER`, so we test this form separately.
42+
if (e._f != null) {
43+
e._f.expectStaticType<Exactly<Object Function()>>();
44+
e._f().expectStaticType<Exactly<Object>>();
45+
}
46+
}
47+
48+
main() {
49+
int Function() f = () => 0;
50+
E(f).testImplicitThisAccess();
51+
E(f).testExplicitThisAccess();
52+
testGeneralPropertyAccess(E(f));
53+
testPrefixedIdentifierAccess(E(f));
54+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
// Tests that the representation variable may undergo field promotion, assuming
6+
// that it's private.
7+
8+
// SharedOptions=--enable-experiment=inline-class
9+
10+
import '../static_type_helper.dart';
11+
12+
extension type E(Object Function() _f) {
13+
testImplicitThisAccess() {
14+
if (_f is int Function()) {
15+
_f.expectStaticType<Exactly<int Function()>>();
16+
_f().expectStaticType<Exactly<int>>();
17+
}
18+
}
19+
20+
testExplicitThisAccess() {
21+
if (this._f is int Function()) {
22+
this._f.expectStaticType<Exactly<int Function()>>();
23+
this._f().expectStaticType<Exactly<int>>();
24+
}
25+
}
26+
}
27+
28+
testGeneralPropertyAccess(E e) {
29+
if ((e)._f is int Function()) {
30+
(e)._f.expectStaticType<Exactly<int Function()>>();
31+
(e)._f().expectStaticType<Exactly<int>>();
32+
}
33+
}
34+
35+
testPrefixedIdentifierAccess(E e) {
36+
// Note: the analyzer has a special representation for property accesses of
37+
// the form `IDENTIFIER.IDENTIFIER`, so we test this form separately.
38+
if (e._f is int Function()) {
39+
e._f.expectStaticType<Exactly<int Function()>>();
40+
e._f().expectStaticType<Exactly<int>>();
41+
}
42+
}
43+
44+
main() {
45+
int Function() f = () => 0;
46+
E(f).testImplicitThisAccess();
47+
E(f).testExplicitThisAccess();
48+
testGeneralPropertyAccess(E(f));
49+
testPrefixedIdentifierAccess(E(f));
50+
}

0 commit comments

Comments
 (0)