Skip to content

Commit ac71a39

Browse files
committed
Add TypeSystemImpl.futureOrBase (), stop using iterable/mapForSetMapDisambiguation.
Change-Id: I44e2888751d4a60fd79468cd184d218f16279637 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/155724 Reviewed-by: Brian Wilkerson <[email protected]>
1 parent a4baaa3 commit ac71a39

File tree

7 files changed

+167
-95
lines changed

7 files changed

+167
-95
lines changed

pkg/analyzer/lib/src/dart/element/type_provider.dart

Lines changed: 0 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -96,9 +96,6 @@ class TypeProviderImpl extends TypeProviderBase {
9696

9797
InterfaceTypeImpl _nullStar;
9898

99-
InterfaceType _iterableForSetMapDisambiguation;
100-
InterfaceType _mapForSetMapDisambiguation;
101-
10299
Set<ClassElement> _nonSubtypableClasses;
103100

104101
/// Initialize a newly created type provider to provide the types defined in
@@ -258,25 +255,6 @@ class TypeProviderImpl extends TypeProviderBase {
258255
return _iterableElement ??= _getClassElement(_coreLibrary, 'Iterable');
259256
}
260257

261-
/// Return the type that should be used during disambiguation between `Set`
262-
/// and `Map` literals. If NNBD enabled, use `Iterable<Object?, Object?>`,
263-
/// otherwise use `Iterable<Object*, Object*>*`.
264-
InterfaceType get iterableForSetMapDisambiguation {
265-
if (_iterableForSetMapDisambiguation == null) {
266-
var objectType = objectElement.instantiate(
267-
typeArguments: const [],
268-
nullabilitySuffix: _questionOrStarSuffix,
269-
);
270-
_iterableForSetMapDisambiguation = iterableElement.instantiate(
271-
typeArguments: [
272-
objectType,
273-
],
274-
nullabilitySuffix: _questionOrStarSuffix,
275-
);
276-
}
277-
return _iterableForSetMapDisambiguation;
278-
}
279-
280258
@override
281259
InterfaceType get iterableObjectType {
282260
_iterableObjectType ??= InterfaceTypeImpl(
@@ -309,26 +287,6 @@ class TypeProviderImpl extends TypeProviderBase {
309287
return _mapElement ??= _getClassElement(_coreLibrary, 'Map');
310288
}
311289

312-
/// Return the type that should be used during disambiguation between `Set`
313-
/// and `Map` literals. If NNBD enabled, use `Map<Object?, Object?>`,
314-
/// otherwise use `Map<Object*, Object*>*`.
315-
InterfaceType get mapForSetMapDisambiguation {
316-
if (_mapForSetMapDisambiguation == null) {
317-
var objectType = objectElement.instantiate(
318-
typeArguments: const [],
319-
nullabilitySuffix: _questionOrStarSuffix,
320-
);
321-
_mapForSetMapDisambiguation = mapElement.instantiate(
322-
typeArguments: [
323-
objectType,
324-
objectType,
325-
],
326-
nullabilitySuffix: _questionOrStarSuffix,
327-
);
328-
}
329-
return _mapForSetMapDisambiguation;
330-
}
331-
332290
@override
333291
InterfaceType get mapObjectObjectType {
334292
_mapObjectObjectType ??= InterfaceTypeImpl(
@@ -491,12 +449,6 @@ class TypeProviderImpl extends TypeProviderBase {
491449
}
492450
}
493451

494-
NullabilitySuffix get _questionOrStarSuffix {
495-
return isNonNullableByDefault
496-
? NullabilitySuffix.question
497-
: NullabilitySuffix.star;
498-
}
499-
500452
@override
501453
InterfaceType futureOrType2(DartType valueType) {
502454
return futureOrElement.instantiate(

pkg/analyzer/lib/src/dart/element/type_system.dart

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,19 @@ abstract class TypeSystem implements public.TypeSystem {
9090
return type;
9191
}
9292

93+
DartType futureOrBase(DartType type) {
94+
// If `T` is `FutureOr<S>` for some `S`,
95+
// then `futureOrBase(T)` = `futureOrBase(S)`
96+
if (type is InterfaceType && type.isDartAsyncFutureOr) {
97+
return futureOrBase(
98+
type.typeArguments[0],
99+
);
100+
}
101+
102+
// Otherwise `futureOrBase(T)` = `T`.
103+
return type;
104+
}
105+
93106
List<InterfaceType> gatherMixinSupertypeConstraintsForInference(
94107
ClassElement mixinElement) {
95108
List<InterfaceType> candidates;

pkg/analyzer/lib/src/dart/resolver/typed_literal_resolver.dart

Lines changed: 64 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import 'package:analyzer/src/dart/ast/ast.dart';
1212
import 'package:analyzer/src/dart/element/element.dart';
1313
import 'package:analyzer/src/dart/element/type.dart';
1414
import 'package:analyzer/src/dart/element/type_provider.dart';
15-
import 'package:analyzer/src/dart/element/type_schema.dart';
1615
import 'package:analyzer/src/dart/element/type_system.dart';
1716
import 'package:analyzer/src/error/codes.dart';
1817
import 'package:analyzer/src/generated/engine.dart';
@@ -267,39 +266,42 @@ class TypedLiteralResolver {
267266
return _LiteralResolution(_LiteralResolutionKind.ambiguous, null);
268267
}
269268

270-
/// If [contextType] is defined and is a subtype of `Iterable<Object>` and
271-
/// [contextType] is not a subtype of `Map<Object, Object>`, then *e* is a set
269+
/// If [contextType] implements `Iterable`, but not `Map`, then *e* is a set
272270
/// literal.
273271
///
274-
/// If [contextType] is defined and is a subtype of `Map<Object, Object>` and
275-
/// [contextType] is not a subtype of `Iterable<Object>` then *e* is a map
272+
/// If [contextType] implements `Map`, but not `Iterable`, then *e* is a map
276273
/// literal.
277274
_LiteralResolution _fromContextType(DartType contextType) {
278275
if (contextType != null) {
279-
DartType unwrap(DartType type) {
280-
if (type is InterfaceType &&
281-
type.isDartAsyncFutureOr &&
282-
type.typeArguments.length == 1) {
283-
return unwrap(type.typeArguments[0]);
284-
}
285-
return type;
286-
}
287-
288-
DartType unwrappedContextType = unwrap(contextType);
276+
var unwrappedContextType = _typeSystem.futureOrBase(contextType);
289277
// TODO(brianwilkerson) Find out what the "greatest closure" is and use that
290278
// where [unwrappedContextType] is used below.
291-
bool isIterable = _typeSystem.isSubtypeOf2(
292-
unwrappedContextType, _typeProvider.iterableForSetMapDisambiguation);
293-
bool isMap = _typeSystem.isSubtypeOf2(
294-
unwrappedContextType, _typeProvider.mapForSetMapDisambiguation);
279+
var iterableType = unwrappedContextType.asInstanceOf(
280+
_typeProvider.iterableElement,
281+
);
282+
var mapType = unwrappedContextType.asInstanceOf(
283+
_typeProvider.mapElement,
284+
);
285+
var isIterable = iterableType != null;
286+
var isMap = mapType != null;
287+
288+
// When `S` implements `Iterable` but not `Map`, `e` is a set literal.
295289
if (isIterable && !isMap) {
296290
return _LiteralResolution(
297-
_LiteralResolutionKind.set, unwrappedContextType);
298-
} else if (isMap && !isIterable) {
291+
_LiteralResolutionKind.set,
292+
unwrappedContextType,
293+
);
294+
}
295+
296+
// When `S` implements `Map` but not `Iterable`, `e` is a map literal.
297+
if (isMap && !isIterable) {
299298
return _LiteralResolution(
300-
_LiteralResolutionKind.map, unwrappedContextType);
299+
_LiteralResolutionKind.map,
300+
unwrappedContextType,
301+
);
301302
}
302303
}
304+
303305
return _LiteralResolution(_LiteralResolutionKind.ambiguous, null);
304306
}
305307

@@ -513,38 +515,54 @@ class TypedLiteralResolver {
513515
} else if (canBeAMap && mustBeAMap) {
514516
return _toMapType(literal, contextType, inferredTypes);
515517
}
518+
516519
// Note: according to the spec, the following computations should be based
517520
// on the greatest closure of the context type (unless the context type is
518521
// `_`). In practice, we can just use the context type directly, because
519522
// the only way the greatest closure of the context type could possibly have
520523
// a different subtype relationship to `Iterable<Object>` and
521524
// `Map<Object, Object>` is if the context type is `_`.
522-
bool contextProvidesAmbiguityResolutionClues =
523-
contextType != null && contextType is! UnknownInferredType;
524-
bool contextIsIterable = contextProvidesAmbiguityResolutionClues &&
525-
_typeSystem.isSubtypeOf2(
526-
contextType, _typeProvider.iterableForSetMapDisambiguation);
527-
bool contextIsMap = contextProvidesAmbiguityResolutionClues &&
528-
_typeSystem.isSubtypeOf2(
529-
contextType, _typeProvider.mapForSetMapDisambiguation);
530-
if (contextIsIterable && !contextIsMap) {
531-
return _toSetType(literal, contextType, inferredTypes);
532-
} else if ((contextIsMap && !contextIsIterable) || elements.isEmpty) {
533-
return _toMapType(literal, contextType, inferredTypes);
534-
} else {
535-
// Ambiguous. We're not going to get any more information to resolve the
536-
// ambiguity. We don't want to make an arbitrary decision at this point
537-
// because it will interfere with future type inference (see
538-
// dartbug.com/36210), so we return a type of `dynamic`.
539-
if (mustBeAMap && mustBeASet) {
540-
_errorReporter.reportErrorForNode(
541-
CompileTimeErrorCode.AMBIGUOUS_SET_OR_MAP_LITERAL_BOTH, literal);
542-
} else {
543-
_errorReporter.reportErrorForNode(
544-
CompileTimeErrorCode.AMBIGUOUS_SET_OR_MAP_LITERAL_EITHER, literal);
525+
if (contextType != null) {
526+
var contextIterableType = contextType.asInstanceOf(
527+
_typeProvider.iterableElement,
528+
);
529+
var contextMapType = contextType.asInstanceOf(
530+
_typeProvider.mapElement,
531+
);
532+
var contextIsIterable = contextIterableType != null;
533+
var contextIsMap = contextMapType != null;
534+
535+
// When `S` implements `Iterable` but not `Map`, `e` is a set literal.
536+
if (contextIsIterable && !contextIsMap) {
537+
return _toSetType(literal, contextType, inferredTypes);
545538
}
546-
return _typeProvider.dynamicType;
539+
540+
// When `S` implements `Map` but not `Iterable`, `e` is a map literal.
541+
if (contextIsMap && !contextIsIterable) {
542+
return _toMapType(literal, contextType, inferredTypes);
543+
}
544+
}
545+
546+
// When `e` is of the form `{}` and `S` is undefined, `e` is a map literal.
547+
if (elements.isEmpty && contextType == null) {
548+
return _typeProvider.mapType2(
549+
DynamicTypeImpl.instance,
550+
DynamicTypeImpl.instance,
551+
);
552+
}
553+
554+
// Ambiguous. We're not going to get any more information to resolve the
555+
// ambiguity. We don't want to make an arbitrary decision at this point
556+
// because it will interfere with future type inference (see
557+
// dartbug.com/36210), so we return a type of `dynamic`.
558+
if (mustBeAMap && mustBeASet) {
559+
_errorReporter.reportErrorForNode(
560+
CompileTimeErrorCode.AMBIGUOUS_SET_OR_MAP_LITERAL_BOTH, literal);
561+
} else {
562+
_errorReporter.reportErrorForNode(
563+
CompileTimeErrorCode.AMBIGUOUS_SET_OR_MAP_LITERAL_EITHER, literal);
547564
}
565+
return _typeProvider.dynamicType;
548566
}
549567

550568
DartType _inferSetTypeDownwards(SetOrMapLiteral node, DartType contextType) {
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
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:test/test.dart';
7+
import 'package:test_reflective_loader/test_reflective_loader.dart';
8+
9+
import '../../../generated/type_system_test.dart';
10+
11+
main() {
12+
defineReflectiveSuite(() {
13+
defineReflectiveTests(FutureOrBaseTest);
14+
});
15+
}
16+
17+
@reflectiveTest
18+
class FutureOrBaseTest extends AbstractTypeSystemNullSafetyTest {
19+
test_dynamic() {
20+
_check(dynamicNone, 'dynamic');
21+
}
22+
23+
test_futureOr() {
24+
void check(DartType S, String expected) {
25+
_check(futureOrNone(S), expected);
26+
}
27+
28+
check(intNone, 'int');
29+
check(intQuestion, 'int?');
30+
check(intStar, 'int*');
31+
32+
check(dynamicNone, 'dynamic');
33+
check(voidNone, 'void');
34+
35+
check(neverNone, 'Never');
36+
check(neverQuestion, 'Never?');
37+
check(neverStar, 'Never*');
38+
39+
check(objectNone, 'Object');
40+
check(objectQuestion, 'Object?');
41+
check(objectStar, 'Object*');
42+
}
43+
44+
test_other() {
45+
_check(intNone, 'int');
46+
_check(intQuestion, 'int?');
47+
_check(intStar, 'int*');
48+
49+
_check(objectNone, 'Object');
50+
_check(objectQuestion, 'Object?');
51+
_check(objectStar, 'Object*');
52+
}
53+
54+
/// futureValueType(`void`) = `void`.
55+
test_void() {
56+
_check(voidNone, 'void');
57+
}
58+
59+
void _check(DartType T, String expected) {
60+
var result = typeSystem.futureOrBase(T);
61+
expect(
62+
result.getDisplayString(withNullability: true),
63+
expected,
64+
);
65+
}
66+
}

pkg/analyzer/test/src/dart/element/test_all.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import 'element_test.dart' as element;
99
import 'factor_type_test.dart' as factor_type;
1010
import 'flatten_type_test.dart' as flatten_type;
1111
import 'function_type_test.dart' as function_type;
12+
import 'future_or_base_test.dart' as future_or_base;
1213
import 'future_value_type_test.dart' as future_value_type;
1314
import 'generic_inferrer_test.dart' as generic_inferrer;
1415
import 'inheritance_manager3_test.dart' as inheritance_manager3;
@@ -33,6 +34,7 @@ main() {
3334
factor_type.main();
3435
flatten_type.main();
3536
function_type.main();
37+
future_or_base.main();
3638
future_value_type.main();
3739
generic_inferrer.main();
3840
inheritance_manager3.main();

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,17 @@ Map<int, int> a = {1 : 2};
4040
assertType(setOrMapLiteral('{'), 'Map<int, int>');
4141
}
4242

43+
test_context_noTypeArgs_noElements_futureOr() async {
44+
await resolveTestCode('''
45+
import 'dart:async';
46+
47+
FutureOr<Map<int, String>> f() {
48+
return {};
49+
}
50+
''');
51+
assertType(setOrMapLiteral('{};'), 'Map<int, String>');
52+
}
53+
4354
test_context_noTypeArgs_noEntries() async {
4455
await assertNoErrorsInCode('''
4556
Map<String, String> a = {};

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

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,17 @@ Set<String> a = {};
4040
assertType(setLiteral('{'), 'Set<String>');
4141
}
4242

43+
test_context_noTypeArgs_noElements_futureOr() async {
44+
await resolveTestCode('''
45+
import 'dart:async';
46+
47+
FutureOr<Set<int>> f() {
48+
return {};
49+
}
50+
''');
51+
assertType(setLiteral('{};'), 'Set<int>');
52+
}
53+
4354
test_context_noTypeArgs_noElements_typeParameter() async {
4455
await resolveTestCode('''
4556
class A<E extends Set<int>> {
@@ -276,7 +287,6 @@ void f<T extends num>(T a) {
276287
assertType(setLiteral('{...'), 'dynamic');
277288
}
278289

279-
280290
test_noContext_typeArgs_expression_conflict() async {
281291
await resolveTestCode('''
282292
var a = <String>{1};

0 commit comments

Comments
 (0)