Skip to content

Commit 8726b35

Browse files
scheglovcommit-bot@chromium.org
authored andcommitted
Issue 40413. Don't infer promoted TypeParameterType(s) for locals.
Internal presubmit looks green. https://test.corp.google.com/ui#id=OCL:295832949:BASE:295984425:1582132722046:8eaca57b Bug: #40413 Change-Id: I8514f6e1a99c4b02098cc649607cf31c5daf350f Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/136360 Reviewed-by: Brian Wilkerson <[email protected]> Commit-Queue: Konstantin Shcheglov <[email protected]>
1 parent 97cf54c commit 8726b35

File tree

5 files changed

+173
-2
lines changed

5 files changed

+173
-2
lines changed
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
// Copyright (c) 2020, 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:analyzer/dart/element/element.dart';
6+
import 'package:analyzer/dart/element/nullability_suffix.dart';
7+
import 'package:analyzer/dart/element/type.dart';
8+
import 'package:analyzer/src/dart/element/replacement_visitor.dart';
9+
import 'package:analyzer/src/dart/element/type.dart';
10+
import 'package:analyzer/src/dart/element/type_visitor.dart';
11+
12+
/// Returns [type] in which all promoted type variables have been replace with
13+
/// their unpromoted equivalents, and, if [library] is non-nullable by default,
14+
/// replaces all legacy types with their non-nullable equivalents.
15+
DartType demoteType(LibraryElement library, DartType type) {
16+
if (library.isNonNullableByDefault) {
17+
var visitor = const _DemotionNonNullification();
18+
return visitor.visit(type) ?? type;
19+
} else {
20+
var visitor = const _DemotionNonNullification(nonNullifyTypes: false);
21+
return visitor.visit(type) ?? type;
22+
}
23+
}
24+
25+
/// Returns `true` if type contains a promoted type variable.
26+
bool hasPromotedTypeVariable(DartType type) {
27+
return const _HasPromotedTypeVariableVisitor()._visit(type);
28+
}
29+
30+
/// Returns [type] in which all legacy types have been replaced with
31+
/// non-nullable types.
32+
DartType nonNullifyType(LibraryElement library, DartType type) {
33+
if (library.isNonNullableByDefault) {
34+
var visitor = const _DemotionNonNullification(demoteTypeVariables: false);
35+
return visitor.visit(type) ?? type;
36+
}
37+
return type;
38+
}
39+
40+
/// Visitor that replaces all promoted type variables the type variable itself
41+
/// and/or replaces all legacy types with non-nullable types.
42+
///
43+
/// The visitor returns `null` if the type wasn't changed.
44+
class _DemotionNonNullification extends ReplacementVisitor {
45+
final bool demoteTypeVariables;
46+
final bool nonNullifyTypes;
47+
48+
const _DemotionNonNullification({
49+
this.demoteTypeVariables = true,
50+
this.nonNullifyTypes = true,
51+
}) : assert(demoteTypeVariables || nonNullifyTypes);
52+
53+
@override
54+
NullabilitySuffix visitNullability(DartType type) {
55+
if (nonNullifyTypes && type.nullabilitySuffix == NullabilitySuffix.star) {
56+
return NullabilitySuffix.none;
57+
}
58+
return null;
59+
}
60+
61+
@override
62+
DartType visitTypeParameterType(TypeParameterType type) {
63+
var newNullability = visitNullability(type);
64+
65+
if (demoteTypeVariables) {
66+
var typeImpl = type as TypeParameterTypeImpl;
67+
if (typeImpl.promotedBound != null) {
68+
return TypeParameterTypeImpl(
69+
element: type.element,
70+
nullabilitySuffix: newNullability ?? type.nullabilitySuffix,
71+
);
72+
}
73+
}
74+
75+
return createTypeParameterType(
76+
type: type,
77+
newNullability: newNullability,
78+
);
79+
}
80+
}
81+
82+
/// Visitor that returns `true` if a type contains a promoted type variable.
83+
class _HasPromotedTypeVariableVisitor extends DartTypeVisitor<bool> {
84+
const _HasPromotedTypeVariableVisitor();
85+
86+
@override
87+
bool defaultDartType(DartType type) => false;
88+
89+
@override
90+
bool visitFunctionType(FunctionType type) {
91+
if (_visit(type.returnType)) {
92+
return true;
93+
}
94+
95+
for (var parameter in type.parameters) {
96+
if (_visit(parameter.type)) {
97+
return true;
98+
}
99+
}
100+
101+
return false;
102+
}
103+
104+
@override
105+
bool visitInterfaceType(InterfaceType type) {
106+
for (var typeArgument in type.typeArguments) {
107+
if (_visit(typeArgument)) {
108+
return true;
109+
}
110+
}
111+
return false;
112+
}
113+
114+
@override
115+
bool visitTypeParameterType(TypeParameterType type) {
116+
return (type as TypeParameterTypeImpl).promotedBound != null;
117+
}
118+
119+
bool _visit(DartType type) {
120+
return DartTypeVisitor.visit(type, this);
121+
}
122+
}

pkg/analyzer/lib/src/generated/static_type_analyzer.dart

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import 'package:analyzer/src/dart/ast/ast.dart';
1212
import 'package:analyzer/src/dart/element/element.dart';
1313
import 'package:analyzer/src/dart/element/member.dart' show ConstructorMember;
1414
import 'package:analyzer/src/dart/element/type.dart';
15+
import 'package:analyzer/src/dart/element/type_demotion.dart';
1516
import 'package:analyzer/src/dart/element/type_provider.dart';
1617
import 'package:analyzer/src/dart/resolver/flow_analysis_visitor.dart';
1718
import 'package:analyzer/src/error/codes.dart';
@@ -985,7 +986,12 @@ class StaticTypeAnalyzer extends SimpleAstVisitor<void> {
985986
if (type != null && !type.isBottom && !type.isDartCoreNull) {
986987
VariableElement element = node.declaredElement;
987988
if (element is LocalVariableElementImpl) {
988-
element.type = initializer.staticType;
989+
var initializerType = initializer.staticType;
990+
var inferredType = demoteType(
991+
_resolver.definingLibrary,
992+
initializerType,
993+
);
994+
element.type = inferredType;
989995
}
990996
}
991997
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// Copyright (c) 2020, 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:analyzer/dart/element/type.dart';
6+
import 'package:analyzer/src/dart/element/type.dart';
7+
import 'package:test/test.dart';
8+
import 'package:test_reflective_loader/test_reflective_loader.dart';
9+
10+
import '../driver_resolution.dart';
11+
12+
main() {
13+
defineReflectiveSuite(() {
14+
defineReflectiveTests(LocalVariableTest);
15+
});
16+
}
17+
18+
@reflectiveTest
19+
class LocalVariableTest extends DriverResolutionTest {
20+
void assertPromotedBound(DartType type, Matcher promotedBound) {
21+
if (type is TypeParameterTypeImpl) {
22+
expect(type.promotedBound, promotedBound);
23+
}
24+
}
25+
26+
test_demoteTypeParameterType() async {
27+
await assertNoErrorsInCode('''
28+
void f<T>(T a, T b) {
29+
if (a is String) {
30+
var o = a;
31+
o = b;
32+
o; // ref
33+
}
34+
}
35+
''');
36+
37+
var type = findNode.simple('o; // ref').staticType;
38+
assertType(type, 'T');
39+
assertPromotedBound(type, isNull);
40+
}
41+
}

pkg/analyzer/test/src/dart/resolution/type_inference/test_all.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import 'equality_expressions_test.dart' as equality_expressions;
1010
import 'extension_methods_test.dart' as extension_methods;
1111
import 'function_expression_test.dart' as function_expression;
1212
import 'list_literal_test.dart' as list_literal;
13+
import 'local_variable_test.dart' as local_variable;
1314
import 'logical_boolean_expressions_test.dart' as logical_boolean_expressions;
1415
import 'map_literal_test.dart' as map_literal;
1516
import 'set_literal_test.dart' as set_literal;
@@ -26,6 +27,7 @@ main() {
2627
extension_methods.main();
2728
function_expression.main();
2829
list_literal.main();
30+
local_variable.main();
2931
logical_boolean_expressions.main();
3032
map_literal.main();
3133
set_literal.main();

pkg/analyzer/test/src/task/strong/checker_test.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4603,7 +4603,7 @@ void f<T extends num>(T x, T y) {
46034603
// This captures the type `T extends int`.
46044604
var g = () => x;
46054605
g = f;
4606-
g().isEven;
4606+
g()./*error:UNDEFINED_GETTER*/isEven;
46074607
q = g();
46084608
int r = x;
46094609
}

0 commit comments

Comments
 (0)