Skip to content

Commit de11b7b

Browse files
johnniwinthercommit-bot@chromium.org
authored andcommitted
[cfe] Implement legacy erasure
Change-Id: I4f152264469e7cb6d23c7502e277004cd35e8527 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/127463 Commit-Queue: Johnni Winther <[email protected]> Reviewed-by: Dmitry Stefantsov <[email protected]>
1 parent 168cf90 commit de11b7b

File tree

7 files changed

+382
-57
lines changed

7 files changed

+382
-57
lines changed
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
// Copyright (c) 2019, 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.md file.
4+
5+
import 'package:kernel/ast.dart' hide MapEntry;
6+
import 'package:kernel/core_types.dart';
7+
8+
import 'replacement_visitor.dart';
9+
10+
/// Returns legacy erasure of [type], that is, the type in which all nnbd
11+
/// nullabilities have been replaced with legacy nullability, and all required
12+
/// named parameters are not required.
13+
DartType legacyErasure(CoreTypes coreTypes, DartType type) {
14+
return type.accept(new _LegacyErasure(coreTypes)) ?? type;
15+
}
16+
17+
/// Visitor that replaces all nnbd nullabilities with legacy nullabilities and
18+
/// all required named parameters with optional named parameters.
19+
///
20+
/// The visitor returns `null` if the type wasn't changed.
21+
class _LegacyErasure extends ReplacementVisitor {
22+
final CoreTypes coreTypes;
23+
24+
_LegacyErasure(this.coreTypes);
25+
26+
@override
27+
NamedType createNamedType(NamedType node, DartType newType) {
28+
if (node.isRequired || newType != null) {
29+
return new NamedType(node.name, newType ?? node.type, isRequired: false);
30+
}
31+
return null;
32+
}
33+
34+
@override
35+
DartType createFunctionType(
36+
FunctionType node,
37+
List<TypeParameter> newTypeParameters,
38+
DartType newReturnType,
39+
List<DartType> newPositionalParameters,
40+
List<NamedType> newNamedParameters,
41+
TypedefType newTypedefType) {
42+
if (node.nullability != Nullability.legacy ||
43+
newTypeParameters != null ||
44+
newReturnType != null ||
45+
newPositionalParameters != null ||
46+
newNamedParameters != null ||
47+
newTypedefType != null) {
48+
return new FunctionType(
49+
newPositionalParameters ?? node.positionalParameters,
50+
newReturnType ?? node.returnType,
51+
Nullability.legacy,
52+
namedParameters: newNamedParameters ?? node.namedParameters,
53+
typeParameters: newTypeParameters ?? node.typeParameters,
54+
requiredParameterCount: node.requiredParameterCount,
55+
typedefType: newTypedefType ?? node.typedefType);
56+
}
57+
return null;
58+
}
59+
60+
@override
61+
DartType createInterfaceType(
62+
InterfaceType node, List<DartType> newTypeArguments) {
63+
if (node.classNode == coreTypes.nullClass) return null;
64+
65+
if (node.nullability != Nullability.legacy || newTypeArguments != null) {
66+
return new InterfaceType(node.classNode, Nullability.legacy,
67+
newTypeArguments ?? node.typeArguments);
68+
}
69+
return null;
70+
}
71+
72+
DartType createTypeParameterType(TypeParameterType node) {
73+
if (node.nullability != Nullability.legacy) {
74+
return new TypeParameterType(node.parameter, Nullability.legacy);
75+
}
76+
return null;
77+
}
78+
79+
DartType createPromotedTypeParameterType(
80+
TypeParameterType node, DartType newPromotedBound) {
81+
if (node.nullability != Nullability.legacy || newPromotedBound != null) {
82+
return new TypeParameterType(
83+
node.parameter, Nullability.legacy, newPromotedBound);
84+
}
85+
return null;
86+
}
87+
88+
@override
89+
DartType visitNeverType(NeverType node) => coreTypes.nullType;
90+
}

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

Lines changed: 92 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// BSD-style license that can be found in the LICENSE.md file.
44

55
import 'package:kernel/ast.dart' hide MapEntry;
6+
import 'package:kernel/type_algebra.dart';
67

78
/// Helper visitor that clones a type if a nested type is replaced, and
89
/// otherwise returns `null`.
@@ -13,33 +14,92 @@ class ReplacementVisitor implements DartTypeVisitor<DartType> {
1314

1415
@override
1516
DartType visitFunctionType(FunctionType node) {
16-
DartType newReturnType = node.returnType.accept(this);
17+
List<TypeParameter> newTypeParameters;
18+
for (int i = 0; i < node.typeParameters.length; i++) {
19+
TypeParameter typeParameter = node.typeParameters[i];
20+
DartType newBound = typeParameter.bound.accept(this);
21+
DartType newDefaultType = typeParameter.defaultType.accept(this);
22+
if (newBound != null || newDefaultType != null) {
23+
newTypeParameters ??= node.typeParameters.toList(growable: false);
24+
newTypeParameters[i] = new TypeParameter(
25+
typeParameter.name,
26+
newBound ?? typeParameter.bound,
27+
newDefaultType ?? typeParameter.defaultType);
28+
}
29+
}
30+
31+
Substitution substitution;
32+
if (newTypeParameters != null) {
33+
List<TypeParameterType> typeParameterTypes =
34+
new List<TypeParameterType>(newTypeParameters.length);
35+
for (int i = 0; i < newTypeParameters.length; i++) {
36+
typeParameterTypes[i] = new TypeParameterType.forAlphaRenaming(
37+
node.typeParameters[i], newTypeParameters[i]);
38+
}
39+
substitution =
40+
Substitution.fromPairs(node.typeParameters, typeParameterTypes);
41+
for (int i = 0; i < newTypeParameters.length; i++) {
42+
newTypeParameters[i].bound =
43+
substitution.substituteType(newTypeParameters[i].bound);
44+
}
45+
}
46+
47+
DartType visitType(DartType type) {
48+
if (type == null) return null;
49+
DartType result = type.accept(this);
50+
if (substitution != null) {
51+
result = substitution.substituteType(result ?? type);
52+
}
53+
return result;
54+
}
55+
56+
DartType newReturnType = visitType(node.returnType);
1757
changeVariance();
1858
List<DartType> newPositionalParameters = null;
1959
for (int i = 0; i < node.positionalParameters.length; i++) {
20-
DartType substitution = node.positionalParameters[i].accept(this);
21-
if (substitution != null) {
60+
DartType newType = visitType(node.positionalParameters[i]);
61+
if (newType != null) {
2262
newPositionalParameters ??=
2363
node.positionalParameters.toList(growable: false);
24-
newPositionalParameters[i] = substitution;
64+
newPositionalParameters[i] = newType;
2565
}
2666
}
2767
List<NamedType> newNamedParameters = null;
2868
for (int i = 0; i < node.namedParameters.length; i++) {
29-
DartType substitution = node.namedParameters[i].type.accept(this);
30-
if (substitution != null) {
69+
DartType newType = visitType(node.namedParameters[i].type);
70+
NamedType newNamedType =
71+
createNamedType(node.namedParameters[i], newType);
72+
if (newNamedType != null) {
3173
newNamedParameters ??= node.namedParameters.toList(growable: false);
32-
newNamedParameters[i] = new NamedType(
33-
node.namedParameters[i].name, substitution,
34-
isRequired: node.namedParameters[i].isRequired);
74+
newNamedParameters[i] = newNamedType;
3575
}
3676
}
3777
changeVariance();
38-
DartType typedefType = node.typedefType?.accept(this);
78+
DartType newTypedefType = visitType(node.typedefType);
79+
80+
return createFunctionType(node, newTypeParameters, newReturnType,
81+
newPositionalParameters, newNamedParameters, newTypedefType);
82+
}
83+
84+
NamedType createNamedType(NamedType node, DartType newType) {
85+
if (newType == null) {
86+
return null;
87+
} else {
88+
return new NamedType(node.name, newType, isRequired: node.isRequired);
89+
}
90+
}
91+
92+
DartType createFunctionType(
93+
FunctionType node,
94+
List<TypeParameter> newTypeParameters,
95+
DartType newReturnType,
96+
List<DartType> newPositionalParameters,
97+
List<NamedType> newNamedParameters,
98+
TypedefType newTypedefType) {
3999
if (newReturnType == null &&
40100
newPositionalParameters == null &&
41101
newNamedParameters == null &&
42-
typedefType == null) {
102+
newTypedefType == null) {
43103
// No types had to be substituted.
44104
return null;
45105
} else {
@@ -48,9 +108,9 @@ class ReplacementVisitor implements DartTypeVisitor<DartType> {
48108
newReturnType ?? node.returnType,
49109
node.nullability,
50110
namedParameters: newNamedParameters ?? node.namedParameters,
51-
typeParameters: node.typeParameters,
111+
typeParameters: newTypeParameters ?? node.typeParameters,
52112
requiredParameterCount: node.requiredParameterCount,
53-
typedefType: typedefType);
113+
typedefType: newTypedefType ?? node.typedefType);
54114
}
55115
}
56116

@@ -64,6 +124,11 @@ class ReplacementVisitor implements DartTypeVisitor<DartType> {
64124
newTypeArguments[i] = substitution;
65125
}
66126
}
127+
return createInterfaceType(node, newTypeArguments);
128+
}
129+
130+
DartType createInterfaceType(
131+
InterfaceType node, List<DartType> newTypeArguments) {
67132
if (newTypeArguments == null) {
68133
// No type arguments needed to be substituted.
69134
return null;
@@ -92,10 +157,20 @@ class ReplacementVisitor implements DartTypeVisitor<DartType> {
92157
DartType visitTypeParameterType(TypeParameterType node) {
93158
if (node.promotedBound != null) {
94159
DartType newPromotedBound = node.promotedBound.accept(this);
95-
if (newPromotedBound != null) {
96-
return new TypeParameterType(node.parameter,
97-
node.typeParameterTypeNullability, newPromotedBound);
98-
}
160+
return createPromotedTypeParameterType(node, newPromotedBound);
161+
}
162+
return createTypeParameterType(node);
163+
}
164+
165+
DartType createTypeParameterType(TypeParameterType node) {
166+
return null;
167+
}
168+
169+
DartType createPromotedTypeParameterType(
170+
TypeParameterType node, DartType newPromotedBound) {
171+
if (newPromotedBound != null) {
172+
return new TypeParameterType(
173+
node.parameter, node.typeParameterTypeNullability, newPromotedBound);
99174
}
100175
return null;
101176
}

pkg/front_end/test/fasta/types/kernel_type_parser.dart

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,12 @@ class KernelFromParsedType implements Visitor<Node, KernelEnvironment> {
169169
arguments[i].accept<Node, KernelEnvironment>(this, environment);
170170
}
171171
if (declaration is Class) {
172+
if (declaration.name == 'Null' &&
173+
declaration.enclosingLibrary.importUri.scheme == 'dart' &&
174+
declaration.enclosingLibrary.importUri.path == 'core' &&
175+
node.parsedNullability != ParsedNullability.nullable) {
176+
throw "Null type must be written as 'Null?'";
177+
}
172178
List<TypeParameter> typeVariables = declaration.typeParameters;
173179
if (kernelArguments.isEmpty && typeVariables.isNotEmpty) {
174180
kernelArguments = new List<DartType>.filled(typeVariables.length, null);
@@ -287,16 +293,14 @@ class KernelFromParsedType implements Visitor<Node, KernelEnvironment> {
287293
positionalParameters
288294
.add(argument.accept<Node, KernelEnvironment>(this, environment));
289295
}
290-
List<Object> optional = node.arguments.optional;
291-
for (int i = 0; i < optional.length; i++) {
292-
ParsedType parsedType = optional[i];
293-
DartType type =
294-
parsedType.accept<Node, KernelEnvironment>(this, environment);
295-
if (node.arguments.optionalAreNamed) {
296-
namedParameters.add(new NamedType(optional[++i], type));
297-
} else {
298-
positionalParameters.add(type);
299-
}
296+
for (ParsedType argument in node.arguments.positional) {
297+
positionalParameters
298+
.add(argument.accept<Node, KernelEnvironment>(this, environment));
299+
}
300+
for (ParsedNamedArgument argument in node.arguments.named) {
301+
namedParameters.add(new NamedType(argument.name,
302+
argument.type.accept<Node, KernelEnvironment>(this, environment),
303+
isRequired: argument.isRequired));
300304
}
301305
}
302306
namedParameters.sort();
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
// Copyright (c) 2019, 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+
import "package:expect/expect.dart" show Expect;
6+
import 'package:front_end/src/fasta/type_inference/legacy_erasure.dart';
7+
8+
import 'package:kernel/ast.dart' hide MapEntry;
9+
import 'package:kernel/core_types.dart';
10+
11+
import "kernel_type_parser.dart";
12+
import "type_parser.dart";
13+
import "mock_sdk.dart" show mockSdk;
14+
15+
class Env {
16+
Component component;
17+
18+
CoreTypes coreTypes;
19+
20+
KernelEnvironment _libraryEnvironment;
21+
22+
Env(String source) {
23+
Uri libraryUri = Uri.parse('memory:main.dart');
24+
Uri coreUri = Uri.parse("dart:core");
25+
KernelEnvironment coreEnvironment = new KernelEnvironment(coreUri, coreUri);
26+
Library coreLibrary =
27+
parseLibrary(coreUri, mockSdk, environment: coreEnvironment);
28+
_libraryEnvironment = new KernelEnvironment(libraryUri, libraryUri)
29+
.extend(coreEnvironment.declarations);
30+
Library library =
31+
parseLibrary(libraryUri, source, environment: _libraryEnvironment);
32+
library.name = "lib";
33+
component = new Component(libraries: <Library>[coreLibrary, library]);
34+
coreTypes = new CoreTypes(component);
35+
}
36+
37+
DartType parseType(String text) {
38+
List<ParsedType> types = parse(text);
39+
return _libraryEnvironment.kernelFromParsedType(types.single);
40+
}
41+
}
42+
43+
const Map<String, String> data = {
44+
'Null?': 'Null?',
45+
'Never': 'Null?',
46+
'Never?': 'Null?',
47+
'void': 'void',
48+
'dynamic': 'dynamic',
49+
'bool': 'bool*',
50+
'bool?': 'bool*',
51+
'bool*': 'bool*',
52+
'List<bool>': 'List<bool*>*',
53+
'List<bool?>': 'List<bool*>*',
54+
'List<bool*>': 'List<bool*>*',
55+
'List<bool>?': 'List<bool*>*',
56+
'List<bool?>?': 'List<bool*>*',
57+
'List<bool*>?': 'List<bool*>*',
58+
'List<bool>*': 'List<bool*>*',
59+
'List<bool?>*': 'List<bool*>*',
60+
'List<bool*>*': 'List<bool*>*',
61+
'() ->* bool*': '() ->* bool*',
62+
'() ->? bool*': '() ->* bool*',
63+
'() -> bool*': '() ->* bool*',
64+
'() ->* bool?': '() ->* bool*',
65+
'() ->? bool?': '() ->* bool*',
66+
'() -> bool?': '() ->* bool*',
67+
'() ->* bool': '() ->* bool*',
68+
'() ->? bool': '() ->* bool*',
69+
'() -> bool': '() ->* bool*',
70+
'(int*) -> void': '(int*) ->* void',
71+
'(int?) -> void': '(int*) ->* void',
72+
'(int) -> void': '(int*) ->* void',
73+
'([int*]) -> void': '([int*]) ->* void',
74+
'([int?]) -> void': '([int*]) ->* void',
75+
'([int]) -> void': '([int*]) ->* void',
76+
'({int* a}) -> void': '({int* a}) ->* void',
77+
'({int? a}) -> void': '({int* a}) ->* void',
78+
'({int a}) -> void': '({int* a}) ->* void',
79+
'({required int* a}) -> void': '({int* a}) ->* void',
80+
'({required int? a}) -> void': '({int* a}) ->* void',
81+
'({required int a}) -> void': '({int* a}) ->* void',
82+
'<T>(T) -> void': '<T extends Object*>(T) ->* void',
83+
'<T>(T?) -> void': '<T extends Object*>(T*) ->* void',
84+
'<T extends bool>(T) -> void': '<T extends bool*>(T) ->* void',
85+
'<T extends List<T>>(T) -> void': '<T extends List<T*>*>(T) ->* void',
86+
};
87+
88+
main() {
89+
Env env = new Env('');
90+
data.forEach((String input, String output) {
91+
DartType inputType = env.parseType(input);
92+
DartType expectedOutputType = env.parseType(output);
93+
DartType actualOutputType = legacyErasure(env.coreTypes, inputType);
94+
print('legacyErasure($inputType) = $actualOutputType: $expectedOutputType');
95+
Expect.equals(
96+
expectedOutputType,
97+
actualOutputType,
98+
"Unexpected legacy erasure of $inputType ('$input'):\n"
99+
"Expected: ${expectedOutputType} ('$output')\n"
100+
"Actual: ${actualOutputType}");
101+
});
102+
}

0 commit comments

Comments
 (0)