Skip to content

Commit 22287dd

Browse files
committed
[Type system] Infer 'Any' for array elements and dictionary values and 'AnyHashable' for dictionary keys.
The id-as-Any work regressed cases where Swift code could specify heterogeneous collection literals, e.g., var states: [String: Any] = [ "California": [ "population": 37_000_000, "cities": ["Los Angeles", "San Diego", "San Jose"], ], "Oregon": [ "population": 4_000_000, "cities": ["Portland", "Salem", "Eugene"], ] ] Prior to this, the code worked (when Foundation was imported) because we'd end up with literals of type [NSObject : AnyObject]. The new defaulting rule says that the element type of an array literal and the key/value types of a dictionary literal can be defaulted if no stronger type can be inferred. The default type is: Any, for the element type of an array literal or the value type of a dictionary literal, or AnyHashable, for the key type of a dictionary literal. The latter is intended to compose with implicit conversions to AnyHashable, so the most-general inferred dictionary type is [AnyHashable : Any] and will work for any plausible dictionary literal. To prevent this inference from diluting types too greatly, we don't allow this inference in "top-level" expressions, e.g., let d = ["a" : 1, "b" : "two"] will produce an error because it's a heterogeneous dictionary literal at the top level. One should annotate this with, e.g., let d = ["a" : 1, "b" : "two"] as [String : Any] However, we do permit heterogeneous collections in nested positions, to support cases like the original motivating example. Fixes rdar://problem/27661580.
1 parent 2bcf555 commit 22287dd

12 files changed

+177
-19
lines changed

lib/Sema/CSApply.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2711,6 +2711,14 @@ namespace {
27112711

27122712
expr->setSemanticExpr(result);
27132713
expr->setType(arrayTy);
2714+
2715+
// If the array element type was defaulted, note that in the expression.
2716+
auto elementTypeVariable = cs.ArrayElementTypeVariables.find(expr);
2717+
if (elementTypeVariable != cs.ArrayElementTypeVariables.end()) {
2718+
if (solution.DefaultedTypeVariables.count(elementTypeVariable->second))
2719+
expr->setIsTypeDefaulted();
2720+
}
2721+
27142722
return expr;
27152723
}
27162724

@@ -2780,6 +2788,18 @@ namespace {
27802788

27812789
expr->setSemanticExpr(result);
27822790
expr->setType(dictionaryTy);
2791+
2792+
// If the dictionary key or value type was defaulted, note that in the
2793+
// expression.
2794+
auto elementTypeVariable = cs.DictionaryElementTypeVariables.find(expr);
2795+
if (elementTypeVariable != cs.DictionaryElementTypeVariables.end()) {
2796+
if (solution.DefaultedTypeVariables.count(
2797+
elementTypeVariable->second.first) ||
2798+
solution.DefaultedTypeVariables.count(
2799+
elementTypeVariable->second.second))
2800+
expr->setIsTypeDefaulted();
2801+
}
2802+
27832803
return expr;
27842804
}
27852805

lib/Sema/CSGen.cpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1728,6 +1728,13 @@ namespace {
17281728
LocatorPathElt::getTupleElement(index++)));
17291729
}
17301730

1731+
// The array element type defaults to 'Any'.
1732+
if (auto elementTypeVar = arrayElementTy->getAs<TypeVariableType>()) {
1733+
CS.addConstraint(ConstraintKind::Defaultable, arrayElementTy,
1734+
tc.Context.TheAnyType, locator);
1735+
CS.ArrayElementTypeVariables[expr] = elementTypeVar;
1736+
}
1737+
17311738
return arrayTy;
17321739
}
17331740

@@ -1849,6 +1856,26 @@ namespace {
18491856
LocatorPathElt::getTupleElement(index++)));
18501857
}
18511858

1859+
// The dictionary key type defaults to 'AnyHashable'.
1860+
auto keyTypeVar = dictionaryKeyTy->getAs<TypeVariableType>();
1861+
if (keyTypeVar && tc.Context.getAnyHashableDecl()) {
1862+
auto anyHashable = tc.Context.getAnyHashableDecl();
1863+
tc.validateDecl(anyHashable);
1864+
CS.addConstraint(ConstraintKind::Defaultable, dictionaryKeyTy,
1865+
anyHashable->getDeclaredInterfaceType(), locator);
1866+
}
1867+
1868+
// The dictionary value type defaults to 'Any'.
1869+
auto valueTypeVar = dictionaryValueTy->getAs<TypeVariableType>();
1870+
if (valueTypeVar) {
1871+
CS.addConstraint(ConstraintKind::Defaultable, dictionaryValueTy,
1872+
tc.Context.TheAnyType, locator);
1873+
}
1874+
1875+
// Record key/value type variables.
1876+
if (keyTypeVar || valueTypeVar)
1877+
CS.DictionaryElementTypeVariables[expr] = { keyTypeVar, valueTypeVar };
1878+
18521879
return dictionaryTy;
18531880
}
18541881

lib/Sema/CSSolver.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,10 @@ Solution ConstraintSystem::finalize(
191191
solution.OpenedExistentialTypes.insert(openedExistential);
192192
}
193193

194+
// Remember the defaulted type variables.
195+
solution.DefaultedTypeVariables.insert(DefaultedTypeVariables.begin(),
196+
DefaultedTypeVariables.end());
197+
194198
return solution;
195199
}
196200

@@ -247,6 +251,10 @@ void ConstraintSystem::applySolution(const Solution &solution) {
247251
OpenedExistentialTypes.push_back(openedExistential);
248252
}
249253

254+
// Register the defaulted type variables.
255+
DefaultedTypeVariables.append(solution.DefaultedTypeVariables.begin(),
256+
solution.DefaultedTypeVariables.end());
257+
250258
// Register any fixes produced along this path.
251259
Fixes.append(solution.Fixes.begin(), solution.Fixes.end());
252260
}
@@ -445,6 +453,7 @@ ConstraintSystem::SolverScope::SolverScope(ConstraintSystem &cs)
445453
numDisjunctionChoices = cs.DisjunctionChoices.size();
446454
numOpenedTypes = cs.OpenedTypes.size();
447455
numOpenedExistentialTypes = cs.OpenedExistentialTypes.size();
456+
numDefaultedTypeVariables = cs.DefaultedTypeVariables.size();
448457
numGeneratedConstraints = cs.solverState->generatedConstraints.size();
449458
PreviousScore = cs.CurrentScore;
450459

@@ -501,6 +510,9 @@ ConstraintSystem::SolverScope::~SolverScope() {
501510
// Remove any opened existential types.
502511
truncate(cs.OpenedExistentialTypes, numOpenedExistentialTypes);
503512

513+
// Remove any defaulted type variables.
514+
truncate(cs.DefaultedTypeVariables, numDefaultedTypeVariables);
515+
504516
// Reset the previous score.
505517
cs.CurrentScore = PreviousScore;
506518

@@ -1182,6 +1194,12 @@ static bool tryTypeVariableBindings(
11821194
typeVar,
11831195
type,
11841196
typeVar->getImpl().getLocator());
1197+
1198+
// If this was from a defaultable binding note that.
1199+
if (binding.IsDefaultableBinding) {
1200+
cs.DefaultedTypeVariables.push_back(typeVar);
1201+
}
1202+
11851203
if (!cs.solveRec(solutions, allowFreeTypeVariables))
11861204
anySolved = true;
11871205

lib/Sema/ConstraintSystem.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -599,6 +599,9 @@ class Solution {
599599
llvm::SmallDenseMap<ConstraintLocator *, ArchetypeType *>
600600
OpenedExistentialTypes;
601601

602+
/// The type variables that were bound via a Defaultable constraint.
603+
llvm::SmallPtrSet<TypeVariableType *, 8> DefaultedTypeVariables;
604+
602605
/// \brief Simplify the given type by substituting all occurrences of
603606
/// type variables for their fixed types.
604607
Type simplifyType(TypeChecker &tc, Type type) const;
@@ -997,6 +1000,23 @@ class ConstraintSystem {
9971000
SmallVector<std::pair<ConstraintLocator *, ArchetypeType *>, 4>
9981001
OpenedExistentialTypes;
9991002

1003+
public:
1004+
/// The type variables that were bound via a Defaultable constraint.
1005+
SmallVector<TypeVariableType *, 8> DefaultedTypeVariables;
1006+
1007+
/// The type variable used to describe the element type of the given array
1008+
/// literal.
1009+
llvm::SmallDenseMap<ArrayExpr *, TypeVariableType *>
1010+
ArrayElementTypeVariables;
1011+
1012+
1013+
/// The type variables used to describe the key and value types of the given
1014+
/// dictionary literal.
1015+
llvm::SmallDenseMap<DictionaryExpr *,
1016+
std::pair<TypeVariableType *, TypeVariableType *>>
1017+
DictionaryElementTypeVariables;
1018+
1019+
private:
10001020
/// \brief Describes the current solver state.
10011021
struct SolverState {
10021022
SolverState(ConstraintSystem &cs);
@@ -1122,6 +1142,9 @@ class ConstraintSystem {
11221142
/// The length of \c OpenedExistentialTypes.
11231143
unsigned numOpenedExistentialTypes;
11241144

1145+
/// The length of \c DefaultedTypeVariables.
1146+
unsigned numDefaultedTypeVariables;
1147+
11251148
/// The previous score.
11261149
Score PreviousScore;
11271150

lib/Sema/MiscDiagnostics.cpp

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -240,10 +240,6 @@ static void diagSyntacticUseRestrictions(TypeChecker &TC, const Expr *E,
240240
while (auto Conv = dyn_cast<ImplicitConversionExpr>(Base))
241241
Base = Conv->getSubExpr();
242242

243-
if (auto collection = dyn_cast<CollectionExpr>(E))
244-
if (collection->isTypeDefaulted())
245-
checkTypeDefaultedCollectionExpr(collection);
246-
247243
// Record call arguments.
248244
if (auto Call = dyn_cast<CallExpr>(Base))
249245
CallArgs.insert(Call->getArg());
@@ -390,10 +386,6 @@ static void diagSyntacticUseRestrictions(TypeChecker &TC, const Expr *E,
390386
/// an error if it was inferred to this type in an invalid context, which is
391387
/// one in which the parent expression is not itself a collection literal.
392388
void checkTypeDefaultedCollectionExpr(CollectionExpr *c) {
393-
if (auto *ParentExpr = Parent.getAsExpr())
394-
if (isa<CollectionExpr>(ParentExpr))
395-
return;
396-
397389
// If the parent is a non-expression, or is not itself a literal, then
398390
// produce an error with a fixit to add the type as an explicit
399391
// annotation.
@@ -703,6 +695,16 @@ static void diagSyntacticUseRestrictions(TypeChecker &TC, const Expr *E,
703695

704696
DiagnoseWalker Walker(TC, DC, isExprStmt);
705697
const_cast<Expr *>(E)->walk(Walker);
698+
699+
// Diagnose uses of collection literals with defaulted types at the top
700+
// level.
701+
if (auto collection
702+
= dyn_cast<CollectionExpr>(E->getSemanticsProvidingExpr())) {
703+
if (collection->isTypeDefaulted()) {
704+
Walker.checkTypeDefaultedCollectionExpr(
705+
const_cast<CollectionExpr *>(collection));
706+
}
707+
}
706708
}
707709

708710

lib/Sema/TypeCheckConstraints.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2634,6 +2634,15 @@ void Solution::dump(raw_ostream &out) const {
26342634
}
26352635
}
26362636

2637+
if (!DefaultedTypeVariables.empty()) {
2638+
out << "\nDefaulted type variables: ";
2639+
interleave(DefaultedTypeVariables, [&](TypeVariableType *typeVar) {
2640+
out << "$T" << typeVar->getID();
2641+
}, [&] {
2642+
out << ", ";
2643+
});
2644+
}
2645+
26372646
if (!Fixes.empty()) {
26382647
out << "\nFixes:\n";
26392648
for (auto &fix : Fixes) {
@@ -2783,6 +2792,15 @@ void ConstraintSystem::print(raw_ostream &out) {
27832792
}
27842793
}
27852794

2795+
if (!DefaultedTypeVariables.empty()) {
2796+
out << "\nDefaulted type variables: ";
2797+
interleave(DefaultedTypeVariables, [&](TypeVariableType *typeVar) {
2798+
out << "$T" << typeVar->getID();
2799+
}, [&] {
2800+
out << ", ";
2801+
});
2802+
}
2803+
27862804
if (failedConstraint) {
27872805
out << "\nFailed constraint:\n";
27882806
out.indent(2);

test/Constraints/array_literal.swift

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,3 +115,31 @@ func rdar25563498_ok<T : ExpressibleByArrayLiteral>(t: T) -> T
115115
let x: T = [1]
116116
return x
117117
}
118+
119+
class A { }
120+
class B : A { }
121+
class C : A { }
122+
123+
/// Check for defaulting the element type to 'Any'.
124+
func defaultToAny(i: Int, s: String) {
125+
let a1 = [1, "a", 3.5]
126+
// expected-error@-1{{heterogenous collection literal could only be inferred to '[Any]'; add explicit type annotation if this is intentional}}
127+
let _: Int = a1 // expected-error{{value of type '[Any]'}}
128+
129+
let a2: Array = [1, "a", 3.5]
130+
// expected-error@-1{{heterogenous collection literal could only be inferred to '[Any]'; add explicit type annotation if this is intentional}}
131+
132+
let _: Int = a2 // expected-error{{value of type '[Any]'}}
133+
134+
let a3 = []
135+
// expected-error@-1{{empty collection literal requires an explicit type}}
136+
137+
let _: Int = a3 // expected-error{{value of type '[Any]'}}
138+
139+
let _: [Any] = [1, "a", 3.5]
140+
let _: [Any] = [1, "a", [3.5, 3.7, 3.9]]
141+
let _: [Any] = [1, "a", [3.5, "b", 3]]
142+
143+
let a4 = [B(), C()]
144+
let _: Int = a4 // expected-error{{value of type '[A]'}}
145+
}

test/Constraints/bridging.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -264,7 +264,7 @@ func rdar19831698() {
264264
// expected-note@-1{{overloads for '+'}}
265265
var v72 = true + true // expected-error{{binary operator '+' cannot be applied to two 'Bool' operands}}
266266
// expected-note @-1 {{overloads for '+' exist with these partially matching parameter lists:}}
267-
var v73 = true + [] // expected-error{{binary operator '+' cannot be applied to operands of type 'Bool' and '[_]'}}
267+
var v73 = true + [] // expected-error{{binary operator '+' cannot be applied to operands of type 'Bool' and '[Any]'}}
268268
// expected-note @-1 {{overloads for '+' exist with these partially matching parameter lists:}}
269269
var v75 = true + "str" // expected-error {{binary operator '+' cannot be applied to operands of type 'Bool' and 'String'}} expected-note {{expected an argument list of type '(String, String)'}}
270270
}

test/Constraints/dictionary_literal.swift

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,3 +62,27 @@ var _: Dictionary<String, Int>? = ["foo" : 1.0] // expected-error {{cannot conv
6262
// <rdar://problem/24058895> QoI: Should handle [] in dictionary contexts better
6363
var _: [Int: Int] = [] // expected-error {{use [:] to get an empty dictionary literal}} {{22-22=:}}
6464

65+
66+
class A { }
67+
class B : A { }
68+
class C : A { }
69+
70+
func testDefaultExistentials() {
71+
let _ = ["a" : 1, "b" : 2.5, "c" : "hello"]
72+
// expected-error@-1{{heterogenous collection literal could only be inferred to 'Dictionary<String, Any>'; add explicit type annotation if this is intentional}}{{46-46= as Dictionary<String, Any>}}
73+
74+
let _: [String : Any] = ["a" : 1, "b" : 2.5, "c" : "hello"]
75+
76+
let d2 = [:]
77+
// expected-error@-1{{empty collection literal requires an explicit type}}
78+
79+
let _: Int = d2 // expected-error{{value of type 'Dictionary<AnyHashable, Any>'}}
80+
81+
let _ = ["a" : 1,
82+
"b" : [ "a", 2, 3.14159 ],
83+
"c" : [ "a" : 2, "b" : 3.5] ]
84+
// expected-error@-3{{heterogenous collection literal could only be inferred to 'Dictionary<String, Any>'; add explicit type annotation if this is intentional}}
85+
86+
let d3 = ["b" : B(), "c" : C()]
87+
let _: Int = d3 // expected-error{{value of type 'Dictionary<String, A>'}}
88+
}

test/Constraints/subscript.swift

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,9 +70,7 @@ extension Int {
7070

7171
let _ = 1["1"] // expected-error {{ambiguous use of 'subscript'}}
7272

73-
74-
// rdar://17687826 - QoI: error message when reducing to an untyped dictionary isn't helpful
75-
let squares = [ 1, 2, 3 ].reduce([:]) { (dict, n) in // expected-error {{unable to infer complex closure return type; add explicit type to disambiguate}} {{51-51=-> [_ : _] }}
73+
let squares = [ 1, 2, 3 ].reduce([:]) { (dict, n) in
7674
var dict = dict
7775
dict[n] = n * n
7876
return dict

test/IDE/complete_at_top_level.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -349,7 +349,7 @@ func resyncParserB6() {}
349349
for i in [] {
350350
#^TOP_LEVEL_STMT_6^#
351351
// TOP_LEVEL_STMT_6: Begin completions
352-
// TOP_LEVEL_STMT_6: Decl[LocalVar]/Local: i[#<<error type>>#]{{; name=.+$}}
352+
// TOP_LEVEL_STMT_6: Decl[LocalVar]/Local: i[#Any#]{{; name=.+$}}
353353
// TOP_LEVEL_STMT_6: End completions
354354
}
355355

test/expr/expressions.swift

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -675,15 +675,15 @@ func unusedExpressionResults() {
675675
//===----------------------------------------------------------------------===//
676676

677677
func arrayLiterals() {
678-
var a = [1,2,3]
679-
var b : [Int] = []
680-
var c = [] // expected-error {{cannot infer type for empty collection literal without a contextual type}}
678+
let _ = [1,2,3]
679+
let _ : [Int] = []
680+
let _ = [] // expected-error {{empty collection literal requires an explicit type}}
681681
}
682682

683683
func dictionaryLiterals() {
684-
var a = [1 : "foo",2 : "bar",3 : "baz"]
685-
var b : Dictionary<Int, String> = [:]
686-
var c = [:] // expected-error {{cannot infer type for empty collection literal without a contextual type}}
684+
let _ = [1 : "foo",2 : "bar",3 : "baz"]
685+
let _: Dictionary<Int, String> = [:]
686+
let _ = [:] // expected-error {{empty collection literal requires an explicit type}}
687687
}
688688

689689
func invalidDictionaryLiteral() {

0 commit comments

Comments
 (0)