Skip to content

Commit d0b6ce5

Browse files
johnniwintherCommit Queue
authored and
Commit Queue
committed
[cfe] Handle call tear-off on non-interface types
Closes #54352 Closes #54339 Change-Id: Ia1562a495b97a7a1b0638667b906e805cc12f829 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/341940 Reviewed-by: Chloe Stefantsova <[email protected]> Commit-Queue: Johnni Winther <[email protected]>
1 parent 150e61a commit d0b6ce5

15 files changed

+1177
-179
lines changed

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

Lines changed: 92 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -467,11 +467,12 @@ abstract class InferenceVisitorBase implements InferenceVisitor {
467467
isNonNullableByDefault: isNonNullableByDefault,
468468
isVoidAllowed: isVoidAllowed,
469469
isExpressionTypePrecise: preciseTypeErrorTemplate != null,
470-
coerceExpression: coerceExpression);
470+
coerceExpression: coerceExpression,
471+
fileOffset: fileOffset);
471472

472473
if (assignabilityResult.needsTearOff) {
473-
TypedTearoff typedTearoff = _tearOffCall(inferenceResult.expression,
474-
inferenceResult.inferredType as InterfaceType, fileOffset);
474+
TypedTearoff typedTearoff = _tearOffCall(
475+
inferenceResult.expression, inferenceResult.inferredType, fileOffset);
475476
inferenceResult = new ExpressionInferenceResult(
476477
typedTearoff.tearoffType, typedTearoff.tearoff);
477478
}
@@ -577,11 +578,12 @@ abstract class InferenceVisitorBase implements InferenceVisitor {
577578
isNonNullableByDefault: isNonNullableByDefault,
578579
isVoidAllowed: isVoidAllowed,
579580
isExpressionTypePrecise: preciseTypeErrorTemplate != null,
580-
coerceExpression: isCoercionAllowed);
581+
coerceExpression: isCoercionAllowed,
582+
fileOffset: fileOffset);
581583

582584
if (assignabilityResult.needsTearOff) {
583-
TypedTearoff typedTearoff = _tearOffCall(inferenceResult.expression,
584-
inferenceResult.inferredType as InterfaceType, fileOffset);
585+
TypedTearoff typedTearoff = _tearOffCall(
586+
inferenceResult.expression, inferenceResult.inferredType, fileOffset);
585587
inferenceResult = new ExpressionInferenceResult(
586588
typedTearoff.tearoffType, typedTearoff.tearoff);
587589
}
@@ -788,10 +790,10 @@ abstract class InferenceVisitorBase implements InferenceVisitor {
788790
}
789791

790792
TypedTearoff _tearOffCall(
791-
Expression expression, InterfaceType expressionType, int fileOffset) {
792-
Class classNode = expressionType.classNode;
793-
Member callMember = membersBuilder.getInterfaceMember(classNode, callName)!;
794-
assert(callMember is Procedure && callMember.kind == ProcedureKind.Method);
793+
Expression expression, DartType expressionType, int fileOffset) {
794+
ObjectAccessTarget target = findInterfaceMember(
795+
expressionType, callName, fileOffset,
796+
isSetter: false);
795797

796798
// Replace expression with:
797799
// `let t = expression in t == null ? null : t.call`
@@ -804,13 +806,44 @@ abstract class InferenceVisitorBase implements InferenceVisitor {
804806
new EqualsNull(new VariableGet(t)..fileOffset = fileOffset)
805807
..fileOffset = fileOffset;
806808

807-
DartType tearoffType =
808-
getGetterTypeForMemberTarget(callMember, expressionType, isSuper: false)
809-
.withDeclaredNullability(expressionType.nullability);
810-
Expression tearOff = new InstanceTearOff(
811-
InstanceAccessKind.Instance, new VariableGet(t), callName,
812-
interfaceTarget: callMember as Procedure, resultType: tearoffType)
813-
..fileOffset = fileOffset;
809+
DartType tearoffType = target.getGetterType(this);
810+
Expression tearOff;
811+
switch (target.kind) {
812+
case ObjectAccessTargetKind.instanceMember:
813+
tearOff = new InstanceTearOff(
814+
InstanceAccessKind.Instance, new VariableGet(t), callName,
815+
interfaceTarget: target.member as Procedure,
816+
resultType: tearoffType)
817+
..fileOffset = fileOffset;
818+
case ObjectAccessTargetKind.extensionTypeMember:
819+
tearOff = new StaticInvocation(
820+
target.tearoffTarget as Procedure,
821+
new Arguments([new VariableGet(t)],
822+
types: target.receiverTypeArguments)
823+
..fileOffset = fileOffset)
824+
..fileOffset = fileOffset;
825+
case ObjectAccessTargetKind.extensionTypeRepresentation:
826+
case ObjectAccessTargetKind.nullableInstanceMember:
827+
case ObjectAccessTargetKind.objectMember:
828+
case ObjectAccessTargetKind.superMember:
829+
case ObjectAccessTargetKind.callFunction:
830+
case ObjectAccessTargetKind.nullableCallFunction:
831+
case ObjectAccessTargetKind.extensionMember:
832+
case ObjectAccessTargetKind.nullableExtensionMember:
833+
case ObjectAccessTargetKind.dynamic:
834+
case ObjectAccessTargetKind.never:
835+
case ObjectAccessTargetKind.invalid:
836+
case ObjectAccessTargetKind.missing:
837+
case ObjectAccessTargetKind.ambiguous:
838+
case ObjectAccessTargetKind.recordIndexed:
839+
case ObjectAccessTargetKind.recordNamed:
840+
case ObjectAccessTargetKind.nullableRecordIndexed:
841+
case ObjectAccessTargetKind.nullableRecordNamed:
842+
case ObjectAccessTargetKind.nullableExtensionTypeMember:
843+
case ObjectAccessTargetKind.nullableExtensionTypeRepresentation:
844+
throw new UnsupportedError("Unexpected call tear-off $target.");
845+
}
846+
814847
ConditionalExpression conditional = new ConditionalExpression(nullCheck,
815848
new NullLiteral()..fileOffset = fileOffset, tearOff, tearoffType);
816849
return new TypedTearoff(
@@ -825,30 +858,53 @@ abstract class InferenceVisitorBase implements InferenceVisitor {
825858
{required bool isNonNullableByDefault,
826859
required bool isVoidAllowed,
827860
required bool isExpressionTypePrecise,
828-
required bool coerceExpression}) {
861+
required bool coerceExpression,
862+
required int fileOffset}) {
829863
// If an interface type is being assigned to a function type, see if we
830864
// should tear off `.call`.
831865
// TODO(paulberry): use resolveTypeParameter. See findInterfaceMember.
832866
bool needsTearoff = false;
833-
if (coerceExpression &&
834-
expressionType is InterfaceType &&
835-
_shouldMaybeTearOffCall(contextType)) {
836-
Class classNode = expressionType.classNode;
837-
Member? callMember =
838-
membersBuilder.getInterfaceMember(classNode, callName);
839-
if (callMember is Procedure && callMember.kind == ProcedureKind.Method) {
840-
if (_shouldTearOffCall(contextType, expressionType)) {
841-
needsTearoff = true;
842-
if (isNonNullableByDefault && expressionType.isPotentiallyNullable) {
843-
return const AssignabilityResult(
844-
AssignabilityKind.unassignableCantTearoff,
845-
needsTearOff: false);
846-
}
847-
expressionType = getGetterTypeForMemberTarget(
848-
callMember, expressionType,
849-
isSuper: false)
850-
.withDeclaredNullability(expressionType.nullability);
867+
if (coerceExpression && _shouldTearOffCall(contextType, expressionType)) {
868+
ObjectAccessTarget target = findInterfaceMember(
869+
expressionType, callName, fileOffset,
870+
isSetter: false);
871+
bool shouldTearOff;
872+
switch (target.kind) {
873+
case ObjectAccessTargetKind.instanceMember:
874+
case ObjectAccessTargetKind.nullableInstanceMember:
875+
Member? member = target.member;
876+
shouldTearOff =
877+
member is Procedure && member.kind == ProcedureKind.Method;
878+
case ObjectAccessTargetKind.extensionTypeMember:
879+
case ObjectAccessTargetKind.nullableExtensionTypeMember:
880+
shouldTearOff = target.tearoffTarget is Procedure;
881+
case ObjectAccessTargetKind.objectMember:
882+
case ObjectAccessTargetKind.superMember:
883+
case ObjectAccessTargetKind.extensionMember:
884+
case ObjectAccessTargetKind.nullableExtensionMember:
885+
case ObjectAccessTargetKind.dynamic:
886+
case ObjectAccessTargetKind.never:
887+
case ObjectAccessTargetKind.invalid:
888+
case ObjectAccessTargetKind.missing:
889+
case ObjectAccessTargetKind.ambiguous:
890+
case ObjectAccessTargetKind.recordIndexed:
891+
case ObjectAccessTargetKind.recordNamed:
892+
case ObjectAccessTargetKind.nullableRecordIndexed:
893+
case ObjectAccessTargetKind.nullableRecordNamed:
894+
case ObjectAccessTargetKind.callFunction:
895+
case ObjectAccessTargetKind.nullableCallFunction:
896+
case ObjectAccessTargetKind.extensionTypeRepresentation:
897+
case ObjectAccessTargetKind.nullableExtensionTypeRepresentation:
898+
shouldTearOff = false;
899+
}
900+
if (shouldTearOff) {
901+
needsTearoff = true;
902+
if (isNonNullableByDefault && target.isNullable) {
903+
return const AssignabilityResult(
904+
AssignabilityKind.unassignableCantTearoff,
905+
needsTearOff: false);
851906
}
907+
expressionType = target.getGetterType(this);
852908
}
853909
}
854910
ImplicitInstantiation? implicitInstantiation;
@@ -3838,19 +3894,6 @@ abstract class InferenceVisitorBase implements InferenceVisitor {
38383894
return false;
38393895
}
38403896

3841-
bool _shouldMaybeTearOffCall(DartType contextType) {
3842-
if (contextType is FutureOrType) {
3843-
contextType = contextType.typeArgument;
3844-
}
3845-
if (contextType is FunctionType) return true;
3846-
if (contextType is InterfaceType &&
3847-
contextType.classReference ==
3848-
typeSchemaEnvironment.functionClass.reference) {
3849-
return true;
3850-
}
3851-
return false;
3852-
}
3853-
38543897
/// Creates an expression the represents the invalid invocation of [name] on
38553898
/// [receiver] with [arguments].
38563899
///
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
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+
extension type Ext(Function it) {
6+
String call() => 'call from Ext: ${it()}';
7+
}
8+
9+
class C {
10+
String call() => 'call from C';
11+
}
12+
13+
extension type Ext2(C c) implements C {}
14+
15+
class D {
16+
Function get getter1 => Ext(C());
17+
Function get getter2 {
18+
var result = Ext(C());
19+
return result;
20+
}
21+
Function get getter3 => Ext2(C());
22+
Function get getter4 {
23+
var result = Ext2(C());
24+
return result;
25+
}
26+
Function method1<T extends C>(T c) {
27+
return c;
28+
}
29+
Function method2<T extends C>(T c) {
30+
var result = Ext(c);
31+
return result;
32+
}
33+
Function method3<T extends Ext>(Ext e) {
34+
return e;
35+
}
36+
Function method4<T extends Ext2>(Ext2 e) {
37+
return e;
38+
}
39+
Function method5<T>(T c) {
40+
if (c is C) {
41+
return c;
42+
}
43+
return () => null;
44+
}
45+
Function method6<T, S extends C>(T c) {
46+
if (c is S) {
47+
return c;
48+
}
49+
return () => null;
50+
}
51+
}
52+
53+
class E {
54+
String get getter1 => Ext(C())();
55+
56+
String get getter2 {
57+
var result = Ext(C())();
58+
return result;
59+
}
60+
61+
String get getter3 => Ext2(C())();
62+
63+
String get getter4 {
64+
var result = Ext2(C())();
65+
return result;
66+
}
67+
68+
String method1<T extends C>(T c) {
69+
return c();
70+
}
71+
72+
String method2<T extends C>(T c) {
73+
var result = Ext(c)();
74+
return result;
75+
}
76+
77+
String method3<T extends Ext>(Ext e) {
78+
return e();
79+
}
80+
81+
String method4<T extends Ext2>(Ext2 e) {
82+
return e();
83+
}
84+
85+
String method5<T>(T c) {
86+
if (c is C) {
87+
return c();
88+
}
89+
return "";
90+
}
91+
92+
String method6<T, S extends C>(T c) {
93+
if (c is S) {
94+
return c();
95+
}
96+
return "";
97+
}
98+
}
99+
100+
void main() {
101+
var d = D();
102+
print(d.getter1());
103+
print(d.getter2());
104+
print(d.getter3());
105+
print(d.getter4());
106+
print(d.method1(C())());
107+
print(d.method2(C())());
108+
print(d.method3(Ext(C()))());
109+
print(d.method4(Ext2(C()))());
110+
print(d.method5(C())());
111+
print(d.method6(C())());
112+
113+
var e = E();
114+
print(e.getter1);
115+
print(e.getter2);
116+
print(e.getter3);
117+
print(e.getter4);
118+
print(e.method1(C()));
119+
print(e.method2(C()));
120+
print(e.method3(Ext(C())));
121+
print(e.method4(Ext2(C())));
122+
print(e.method5(C()));
123+
print(e.method6(C()));
124+
}

0 commit comments

Comments
 (0)