Skip to content

Commit 63133a2

Browse files
johnniwinthercommit-bot@chromium.org
authored andcommitted
[cfe] Handle extension instance method tearoff
Change-Id: I56f422ed94fbc328030dce1b01df81954969bb01 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/114506 Commit-Queue: Johnni Winther <[email protected]> Reviewed-by: Dmitry Stefantsov <[email protected]>
1 parent a147d4e commit 63133a2

15 files changed

+467
-65
lines changed

pkg/front_end/lib/src/fasta/kernel/body_builder.dart

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1209,7 +1209,7 @@ class BodyBuilder extends ScopeListener<JumpTarget>
12091209
.withLocation(uri, eof.charOffset, eof.length));
12101210
}
12111211

1212-
ReturnJudgment fakeReturn = new ReturnJudgment(null, expression);
1212+
ReturnJudgment fakeReturn = new ReturnJudgment(true, expression);
12131213

12141214
typeInferrer?.inferFunctionBody(
12151215
this, const DynamicType(), AsyncMarker.Sync, fakeReturn);
@@ -1787,12 +1787,10 @@ class BodyBuilder extends ScopeListener<JumpTarget>
17871787

17881788
@override
17891789
Expression createExtensionTearOff(
1790+
int fileOffset,
17901791
Procedure procedure,
17911792
VariableDeclaration extensionThis,
1792-
List<TypeParameter> extensionTypeParameters,
1793-
Token token) {
1794-
int fileOffset = offsetForToken(token);
1795-
1793+
List<TypeParameter> extensionTypeParameters) {
17961794
FunctionNode function = procedure.function;
17971795
List<TypeParameter> typeParameters = [];
17981796
List<DartType> typeArguments = [];
@@ -1864,15 +1862,15 @@ class BodyBuilder extends ScopeListener<JumpTarget>
18641862
}
18651863

18661864
Statement body = forest.createReturnStatement(
1867-
null,
1865+
fileOffset,
18681866
buildStaticInvocation(
18691867
procedure,
18701868
forest.createArguments(fileOffset, positionalArguments,
18711869
types: typeArguments, named: namedArguments),
1872-
charOffset: fileOffset),
1873-
fileOffset);
1870+
charOffset: fileOffset));
18741871

18751872
FunctionExpression expression = forest.createFunctionExpression(
1873+
fileOffset,
18761874
forest.createFunctionNode(body,
18771875
typeParameters: typeParameters,
18781876
positionalParameters: positionalParameters,
@@ -1881,8 +1879,7 @@ class BodyBuilder extends ScopeListener<JumpTarget>
18811879
procedure.function.requiredParameterCount - 1,
18821880
returnType: returnType,
18831881
asyncMarker: procedure.function.asyncMarker,
1884-
dartAsyncMarker: procedure.function.dartAsyncMarker),
1885-
fileOffset);
1882+
dartAsyncMarker: procedure.function.dartAsyncMarker));
18861883
functionNestingLevel--;
18871884
return expression;
18881885
}
@@ -2211,8 +2208,8 @@ class BodyBuilder extends ScopeListener<JumpTarget>
22112208
push(buildProblemStatement(
22122209
fasta.messageConstructorWithReturnType, beginToken.charOffset));
22132210
} else {
2214-
push(forest.createReturnStatement(
2215-
beginToken, expression, offsetForToken(beginToken)));
2211+
push(forest.createReturnStatement(offsetForToken(beginToken), expression,
2212+
isArrow: !identical(beginToken.lexeme, "return")));
22162213
}
22172214
}
22182215

pkg/front_end/lib/src/fasta/kernel/expression_generator.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1697,7 +1697,7 @@ class ExtensionInstanceAccessGenerator extends Generator {
16971697
}
16981698
} else if (readTargetIsRegularMethod) {
16991699
read = _helper.createExtensionTearOff(
1700-
readTarget, extensionThis, extensionTypeParameters, token);
1700+
fileOffset, readTarget, extensionThis, extensionTypeParameters);
17011701
} else {
17021702
List<DartType> typeArguments;
17031703
if (extensionTypeParameters != null) {

pkg/front_end/lib/src/fasta/kernel/expression_generator_helper.dart

Lines changed: 0 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -164,37 +164,4 @@ abstract class ExpressionGeneratorHelper implements InferenceHelper {
164164
/// Creates a [VariableGet] of the [variable] using [charOffset] as the file
165165
/// offset of the created node.
166166
Expression createVariableGet(VariableDeclaration variable, int charOffset);
167-
168-
/// Creates a tear off of the extension instance method [procedure].
169-
///
170-
/// The tear off is created as a function expression that captures the
171-
/// current `this` value from [extensionThis] and [extensionTypeParameters]
172-
/// synthetically copied to the extension instance method.
173-
///
174-
/// For instance the declaration of `B.m`:
175-
///
176-
/// class A<X, Y> {}
177-
/// class B<S, T> on A<S, T> {
178-
/// void m<U>(U u) {}
179-
/// }
180-
///
181-
/// is converted into this top level method:
182-
///
183-
/// void B<S,T>|m<U>(A<S, T> #this, U u) {}
184-
///
185-
/// and a tear off
186-
///
187-
/// A<X, Y> a = ...;
188-
/// var f = a.m;
189-
///
190-
/// is converted into:
191-
///
192-
/// A<int, String> a = ...;
193-
/// var f = <#U>(#U u) => B<S,T>|m<int,String,#U>(a, u);
194-
///
195-
Expression createExtensionTearOff(
196-
Procedure procedure,
197-
VariableDeclaration extensionThis,
198-
List<TypeParameter> extensionTypeParameters,
199-
Token token);
200167
}

pkg/front_end/lib/src/fasta/kernel/forest.dart

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import 'dart:core' hide MapEntry;
88

99
import 'package:kernel/ast.dart';
1010

11+
import '../names.dart';
12+
1113
import '../parser.dart' show offsetForToken, optional;
1214

1315
import '../problems.dart' show unsupported;
@@ -42,6 +44,7 @@ import 'kernel_shadow_ast.dart'
4244
ListLiteralJudgment,
4345
LoadLibraryJudgment,
4446
MapLiteralJudgment,
47+
MethodInvocationJudgment,
4548
ReturnJudgment,
4649
SetLiteralJudgment,
4750
ShadowLargeIntLiteral,
@@ -495,10 +498,10 @@ class Forest {
495498
}
496499

497500
/// Return a representation of a return statement.
498-
Statement createReturnStatement(
499-
Token returnKeyword, Expression expression, int charOffset) {
500-
return new ReturnJudgment(returnKeyword?.lexeme, expression)
501-
..fileOffset = charOffset;
501+
Statement createReturnStatement(int fileOffset, Expression expression,
502+
{bool isArrow: true}) {
503+
return new ReturnJudgment(isArrow, expression)
504+
..fileOffset = fileOffset ?? TreeNode.noOffset;
502505
}
503506

504507
Expression createStringConcatenation(
@@ -686,8 +689,15 @@ class Forest {
686689
}
687690

688691
FunctionExpression createFunctionExpression(
689-
FunctionNode function, int fileOffset) {
690-
return new FunctionExpression(function)..fileOffset = fileOffset;
692+
int fileOffset, FunctionNode function) {
693+
return new FunctionExpression(function)
694+
..fileOffset = fileOffset ?? TreeNode.noOffset;
695+
}
696+
697+
MethodInvocation createFunctionInvocation(
698+
int fileOffset, Expression expression, Arguments arguments) {
699+
return new MethodInvocationJudgment(expression, callName, arguments)
700+
..fileOffset = fileOffset ?? TreeNode.noOffset;
691701
}
692702

693703
NamedExpression createNamedExpression(String name, Expression expression) {

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1766,8 +1766,7 @@ class InferenceVisitor
17661766
} else {
17671767
inferredType = inferrer.coreTypes.nullClass.rawType;
17681768
}
1769-
closureContext.handleReturn(inferrer, node, inferredType,
1770-
!identical(node.returnKeywordLexeme, "return"));
1769+
closureContext.handleReturn(inferrer, node, inferredType, node.isArrow);
17711770
}
17721771

17731772
ExpressionInferenceResult visitSetLiteralJudgment(

pkg/front_end/lib/src/fasta/kernel/kernel_shadow_ast.dart

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1095,10 +1095,9 @@ class RedirectingInitializerJudgment extends RedirectingInitializer
10951095

10961096
/// Concrete shadow object representing a return statement in kernel form.
10971097
class ReturnJudgment extends ReturnStatement implements StatementJudgment {
1098-
final String returnKeywordLexeme;
1098+
final bool isArrow;
10991099

1100-
ReturnJudgment(this.returnKeywordLexeme, [Expression expression])
1101-
: super(expression);
1100+
ReturnJudgment(this.isArrow, [Expression expression]) : super(expression);
11021101

11031102
Expression get judgment => expression;
11041103

@@ -1282,9 +1281,10 @@ class SyntheticExpressionJudgment extends Let implements ExpressionJudgment {
12821281

12831282
/// Removes this expression from the expression tree, replacing it with
12841283
/// [desugared].
1285-
void _replaceWithDesugared() {
1284+
Expression _replaceWithDesugared() {
12861285
parent.replaceChild(this, desugared);
12871286
parent = null;
1287+
return desugared;
12881288
}
12891289

12901290
/// Updates any [Let] nodes in the desugared expression to account for the

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

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22
// for details. All rights reserved. Use of this source code is governed by a
33
// BSD-style license that can be found in the LICENSE file.
44

5-
import 'package:kernel/ast.dart' show Arguments, Expression, FunctionType;
5+
import 'dart:core' hide MapEntry;
6+
7+
import 'package:kernel/ast.dart';
68

79
import 'package:kernel/core_types.dart' show CoreTypes;
810

@@ -35,4 +37,37 @@ abstract class InferenceHelper {
3537
{String className, bool isSuper});
3638

3739
Expression desugarSyntheticExpression(Expression node);
40+
41+
/// Creates a tear off of the extension instance method [procedure].
42+
///
43+
/// The tear off is created as a function expression that captures the
44+
/// current `this` value from [extensionThis] and [extensionTypeParameters]
45+
/// synthetically copied to the extension instance method.
46+
///
47+
/// For instance the declaration of `B.m`:
48+
///
49+
/// class A<X, Y> {}
50+
/// class B<S, T> on A<S, T> {
51+
/// void m<U>(U u) {}
52+
/// }
53+
///
54+
/// is converted into this top level method:
55+
///
56+
/// void B<S,T>|m<U>(A<S, T> #this, U u) {}
57+
///
58+
/// and a tear off
59+
///
60+
/// A<X, Y> a = ...;
61+
/// var f = a.m;
62+
///
63+
/// is converted into:
64+
///
65+
/// A<int, String> a = ...;
66+
/// var f = <#U>(#U u) => B<S,T>|m<int,String,#U>(a, u);
67+
///
68+
Expression createExtensionTearOff(
69+
int fileOffset,
70+
Procedure procedure,
71+
VariableDeclaration extensionThis,
72+
List<TypeParameter> extensionTypeParameters);
3873
}

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

Lines changed: 72 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1852,18 +1852,86 @@ abstract class TypeInferrerImpl extends TypeInferrer {
18521852
instantiateTearOff(inferredType, typeContext, replacedExpression);
18531853
}
18541854
} else if (readTarget.isExtensionMember) {
1855+
int fileOffset = expression.fileOffset;
18551856
switch (readTarget.extensionMethodKind) {
18561857
case kernel.ProcedureKind.Getter:
18571858
expression.parent.replaceChild(
18581859
expression,
18591860
replacement = expression = helper.forest.createStaticInvocation(
1860-
expression.fileOffset,
1861+
fileOffset,
18611862
readTarget.member,
1862-
helper.forest
1863-
.createArguments(expression.fileOffset, [receiver])));
1863+
helper.forest.createArguments(fileOffset, [receiver])));
18641864
break;
18651865
case kernel.ProcedureKind.Method:
1866-
// TODO(johnniwinther): Handle extension method tearoff.
1866+
1867+
// Create instance method tear off by converting
1868+
//
1869+
// extension E on A {
1870+
// method() {}
1871+
// }
1872+
// A a = ...
1873+
// a.method;
1874+
//
1875+
// into
1876+
//
1877+
// E|method(A #this) {}
1878+
// A a ...
1879+
// let #1 = a in () => E|method(#1);
1880+
//
1881+
1882+
//VariableDeclaration variable = helper.forest
1883+
// .createVariableDeclarationForValue(fileOffset, receiver,
1884+
// type: receiverType);
1885+
//expression.parent.replaceChild(
1886+
// expression,
1887+
// replacement = expression = helper.forest.createLet(
1888+
// variable,
1889+
// helper.createExtensionTearOff(
1890+
// expression.fileOffset,
1891+
// readTarget.member,
1892+
// variable,
1893+
// // TODO(johnniwinther): Support generic extensions.
1894+
// [],
1895+
// receiver)));
1896+
1897+
// TODO(johnniwinther): The encoding above doesn't work on the VM
1898+
// (silently stops execution!) and dart2js (crashes due to invalid
1899+
// closure scope result). We use this instead:
1900+
1901+
// Create instance method tear off by converting
1902+
//
1903+
// extension E on A {
1904+
// method() {}
1905+
// }
1906+
// A a = ...
1907+
// a.method;
1908+
//
1909+
// into
1910+
//
1911+
// E|method(A #this) {}
1912+
// A a ...
1913+
// ((#1) => () => E|method(#1))(a);
1914+
//
1915+
1916+
VariableDeclaration variable = helper.forest
1917+
.createVariableDeclaration("#", 0,
1918+
isFinal: true, type: receiverType);
1919+
expression.parent.replaceChild(
1920+
expression,
1921+
replacement = expression = helper.forest.createFunctionInvocation(
1922+
fileOffset,
1923+
helper.forest.createFunctionExpression(
1924+
fileOffset,
1925+
helper.forest.createFunctionNode(
1926+
helper.forest.createReturnStatement(
1927+
fileOffset,
1928+
helper.createExtensionTearOff(
1929+
fileOffset, readTarget.member, variable, [])),
1930+
positionalParameters: [variable],
1931+
requiredParameterCount: 1,
1932+
)),
1933+
helper.forest.createArguments(fileOffset, [receiver])));
1934+
break;
18671935
case kernel.ProcedureKind.Setter:
18681936
case kernel.ProcedureKind.Factory:
18691937
case kernel.ProcedureKind.Operator:

pkg/front_end/test/spell_checking_list_common.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -607,6 +607,7 @@ covered
607607
covers
608608
crash
609609
crashed
610+
crashes
610611
crashing
611612
create
612613
created
@@ -2418,6 +2419,7 @@ signature
24182419
signatures
24192420
signed
24202421
silent
2422+
silently
24212423
silly
24222424
similar
24232425
similarly

0 commit comments

Comments
 (0)