Skip to content

Commit 7c1f58f

Browse files
johnniwinthercommit-bot@chromium.org
authored andcommitted
[cfe] Demote inferred type variables when used as type arguments
Closes #39346 Change-Id: I997347d04e7a3c3cb9786f31cd6c24513307de92 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/125502 Reviewed-by: Dmitry Stefantsov <[email protected]> Commit-Queue: Johnni Winther <[email protected]>
1 parent 16af4b7 commit 7c1f58f

19 files changed

Lines changed: 541 additions & 172 deletions

pkg/front_end/lib/src/fasta/fasta_codes_cfe_generated.dart

Lines changed: 0 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1279,46 +1279,6 @@ Message _withArgumentsInitializingFormalTypeMismatch(
12791279
arguments: {'name': name, 'type': _type, 'type2': _type2});
12801280
}
12811281

1282-
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
1283-
const Template<
1284-
Message Function(
1285-
String name,
1286-
DartType _type,
1287-
DartType
1288-
_type2)> templateIntersectionTypeAsTypeArgument = const Template<
1289-
Message Function(String name, DartType _type, DartType _type2)>(
1290-
messageTemplate:
1291-
r"""Can't infer a type for '#name', it can be either '#type' or '#type2'.""",
1292-
tipTemplate:
1293-
r"""Try adding a type argument selecting one of the options.""",
1294-
withArguments: _withArgumentsIntersectionTypeAsTypeArgument);
1295-
1296-
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
1297-
const Code<Message Function(String name, DartType _type, DartType _type2)>
1298-
codeIntersectionTypeAsTypeArgument =
1299-
const Code<Message Function(String name, DartType _type, DartType _type2)>(
1300-
"IntersectionTypeAsTypeArgument",
1301-
templateIntersectionTypeAsTypeArgument,
1302-
);
1303-
1304-
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
1305-
Message _withArgumentsIntersectionTypeAsTypeArgument(
1306-
String name, DartType _type, DartType _type2) {
1307-
if (name.isEmpty) throw 'No name provided';
1308-
name = demangleMixinApplicationName(name);
1309-
TypeLabeler labeler = new TypeLabeler();
1310-
List<Object> typeParts = labeler.labelType(_type);
1311-
List<Object> type2Parts = labeler.labelType(_type2);
1312-
String type = typeParts.join();
1313-
String type2 = type2Parts.join();
1314-
return new Message(codeIntersectionTypeAsTypeArgument,
1315-
message:
1316-
"""Can't infer a type for '${name}', it can be either '${type}' or '${type2}'.""" +
1317-
labeler.originMessages,
1318-
tip: """Try adding a type argument selecting one of the options.""",
1319-
arguments: {'name': name, 'type': _type, 'type2': _type2});
1320-
}
1321-
13221282
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
13231283
const Template<
13241284
Message Function(

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

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,6 @@ import '../fasta_codes.dart'
145145
templateIncorrectTypeArgumentInferred,
146146
templateIncorrectTypeArgumentQualified,
147147
templateIncorrectTypeArgumentQualifiedInferred,
148-
templateIntersectionTypeAsTypeArgument,
149148
templateLanguageVersionTooHigh,
150149
templateLoadLibraryHidesMember,
151150
templateLocalDefinitionHidesExport,
@@ -2618,15 +2617,6 @@ class SourceLibraryBuilder extends LibraryBuilderImpl {
26182617
message = messageGenericFunctionTypeUsedAsActualTypeArgument;
26192618
}
26202619
typeParameter = null;
2621-
} else if (argument is TypeParameterType &&
2622-
argument.promotedBound != null) {
2623-
addProblem(
2624-
templateIntersectionTypeAsTypeArgument.withArguments(
2625-
typeParameter.name, argument, argument.promotedBound),
2626-
offset,
2627-
noLength,
2628-
fileUri);
2629-
continue;
26302620
} else {
26312621
if (issue.enclosingType == null && targetReceiver != null) {
26322622
if (issueInferred) {
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
// Copyright (c) 2017, 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+
7+
/// Helper visitor that clones a type if a nested type is replaced, and
8+
/// otherwise returns `null`.
9+
class ReplacementVisitor implements DartTypeVisitor<DartType> {
10+
const ReplacementVisitor();
11+
12+
void changeVariance() {}
13+
14+
@override
15+
DartType visitFunctionType(FunctionType node) {
16+
DartType newReturnType = node.returnType.accept(this);
17+
changeVariance();
18+
List<DartType> newPositionalParameters = null;
19+
for (int i = 0; i < node.positionalParameters.length; i++) {
20+
DartType substitution = node.positionalParameters[i].accept(this);
21+
if (substitution != null) {
22+
newPositionalParameters ??=
23+
node.positionalParameters.toList(growable: false);
24+
newPositionalParameters[i] = substitution;
25+
}
26+
}
27+
List<NamedType> newNamedParameters = null;
28+
for (int i = 0; i < node.namedParameters.length; i++) {
29+
DartType substitution = node.namedParameters[i].type.accept(this);
30+
if (substitution != null) {
31+
newNamedParameters ??= node.namedParameters.toList(growable: false);
32+
newNamedParameters[i] = new NamedType(
33+
node.namedParameters[i].name, substitution,
34+
isRequired: node.namedParameters[i].isRequired);
35+
}
36+
}
37+
changeVariance();
38+
DartType typedefType = node.typedefType?.accept(this);
39+
if (newReturnType == null &&
40+
newPositionalParameters == null &&
41+
newNamedParameters == null &&
42+
typedefType == null) {
43+
// No types had to be substituted.
44+
return null;
45+
} else {
46+
return new FunctionType(
47+
newPositionalParameters ?? node.positionalParameters,
48+
newReturnType ?? node.returnType,
49+
node.nullability,
50+
namedParameters: newNamedParameters ?? node.namedParameters,
51+
typeParameters: node.typeParameters,
52+
requiredParameterCount: node.requiredParameterCount,
53+
typedefType: typedefType);
54+
}
55+
}
56+
57+
@override
58+
DartType visitInterfaceType(InterfaceType node) {
59+
List<DartType> newTypeArguments = null;
60+
for (int i = 0; i < node.typeArguments.length; i++) {
61+
DartType substitution = node.typeArguments[i].accept(this);
62+
if (substitution != null) {
63+
newTypeArguments ??= node.typeArguments.toList(growable: false);
64+
newTypeArguments[i] = substitution;
65+
}
66+
}
67+
if (newTypeArguments == null) {
68+
// No type arguments needed to be substituted.
69+
return null;
70+
} else {
71+
return new InterfaceType(
72+
node.classNode, node.nullability, newTypeArguments);
73+
}
74+
}
75+
76+
@override
77+
DartType visitDynamicType(DynamicType node) => null;
78+
79+
@override
80+
DartType visitNeverType(NeverType node) => null;
81+
82+
@override
83+
DartType visitInvalidType(InvalidType node) => null;
84+
85+
@override
86+
DartType visitBottomType(BottomType node) => null;
87+
88+
@override
89+
DartType visitVoidType(VoidType node) => null;
90+
91+
@override
92+
DartType visitTypeParameterType(TypeParameterType node) {
93+
if (node.promotedBound != null) {
94+
DartType newPromotedBound = node.promotedBound.accept(this);
95+
if (newPromotedBound != null) {
96+
return new TypeParameterType(node.parameter,
97+
node.typeParameterTypeNullability, newPromotedBound);
98+
}
99+
}
100+
return null;
101+
}
102+
103+
@override
104+
DartType visitTypedefType(TypedefType node) {
105+
List<DartType> newTypeArguments = null;
106+
for (int i = 0; i < node.typeArguments.length; i++) {
107+
DartType substitution = node.typeArguments[i].accept(this);
108+
if (substitution != null) {
109+
newTypeArguments ??= node.typeArguments.toList(growable: false);
110+
newTypeArguments[i] = substitution;
111+
}
112+
}
113+
if (newTypeArguments == null) {
114+
// No type arguments needed to be substituted.
115+
return null;
116+
} else {
117+
return new TypedefType(
118+
node.typedefNode, node.nullability, newTypeArguments);
119+
}
120+
}
121+
122+
@override
123+
DartType defaultDartType(DartType node) => null;
124+
}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
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+
7+
import 'replacement_visitor.dart';
8+
9+
/// Returns `true` if type contains a promoted type variable.
10+
bool hasPromotedTypeVariable(DartType type) {
11+
return type.accept(const _HasPromotedTypeVariableVisitor());
12+
}
13+
14+
/// Visitor that returns `true` if a type contains a promoted type variable.
15+
class _HasPromotedTypeVariableVisitor extends DartTypeVisitor<bool> {
16+
const _HasPromotedTypeVariableVisitor();
17+
18+
@override
19+
bool defaultDartType(DartType node) => false;
20+
21+
@override
22+
bool visitFunctionType(FunctionType node) {
23+
if (node.returnType.accept(this)) return true;
24+
for (DartType parameterType in node.positionalParameters) {
25+
if (parameterType.accept(this)) return true;
26+
}
27+
for (NamedType namedParameterType in node.namedParameters) {
28+
if (namedParameterType.type.accept(this)) return true;
29+
}
30+
if (node.typedefType != null && node.typedefType.accept(this)) {
31+
return true;
32+
}
33+
return false;
34+
}
35+
36+
@override
37+
bool visitInterfaceType(InterfaceType node) {
38+
for (DartType typeArgument in node.typeArguments) {
39+
if (typeArgument.accept(this)) return true;
40+
}
41+
return false;
42+
}
43+
44+
@override
45+
bool visitTypedefType(TypedefType node) {
46+
for (DartType typeArgument in node.typeArguments) {
47+
if (typeArgument.accept(this)) return true;
48+
}
49+
return false;
50+
}
51+
52+
@override
53+
bool visitTypeParameterType(TypeParameterType node) {
54+
return node.promotedBound != null;
55+
}
56+
}
57+
58+
/// Returns [type] in which all promoted type variables have been replace with
59+
/// their unpromoted equivalents.
60+
DartType demoteType(DartType type) {
61+
return type.accept(const _TypeVariableDemotion()) ?? type;
62+
}
63+
64+
/// Visitor that replaces all promoted type variables the type variable itself.
65+
///
66+
/// The visitor returns `null` if the type wasn't changed.
67+
class _TypeVariableDemotion extends ReplacementVisitor {
68+
const _TypeVariableDemotion();
69+
70+
@override
71+
DartType visitTypeParameterType(TypeParameterType node) {
72+
if (node.promotedBound != null) {
73+
return new TypeParameterType(
74+
node.parameter, node.typeParameterTypeNullability);
75+
}
76+
return node;
77+
}
78+
}

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import 'dart:core' hide MapEntry;
77
import 'package:_fe_analyzer_shared/src/flow_analysis/flow_analysis.dart';
88

99
import 'package:front_end/src/fasta/kernel/kernel_shadow_ast.dart';
10+
import 'package:front_end/src/fasta/type_inference/type_demotion.dart';
1011

1112
import 'package:kernel/ast.dart';
1213

@@ -1787,6 +1788,8 @@ class TypeInferrerImpl implements TypeInferrer {
17871788
inferredTypes);
17881789
assert(inferredTypes.every((type) => isKnown(type)),
17891790
"Unknown type(s) in inferred types: $inferredTypes.");
1791+
assert(inferredTypes.every((type) => !hasPromotedTypeVariable(type)),
1792+
"Promoted type variable(s) in inferred types: $inferredTypes.");
17901793
substitution =
17911794
Substitution.fromPairs(calleeTypeParameters, inferredTypes);
17921795
instrumentation?.record(uriForInstrumentation, offset, 'typeArgs',

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import 'package:kernel/text/ast_to_text.dart'
2020
show Annotator, NameSystem, Printer, globalDebuggingNames;
2121

2222
/// Determines whether a type schema contains `?` somewhere inside it.
23-
bool isKnown(DartType schema) => schema.accept(new _IsKnownVisitor());
23+
bool isKnown(DartType schema) => schema.accept(const _IsKnownVisitor());
2424

2525
/// Converts a [DartType] to a string, representing the unknown type as `?`.
2626
String typeSchemaToString(DartType schema) {
@@ -87,6 +87,8 @@ class UnknownType extends DartType {
8787

8888
/// Visitor that computes [isKnown].
8989
class _IsKnownVisitor extends DartTypeVisitor<bool> {
90+
const _IsKnownVisitor();
91+
9092
@override
9193
bool defaultDartType(DartType node) => node is! UnknownType;
9294

0 commit comments

Comments
 (0)