Skip to content

Commit 2cdd7d6

Browse files
committed
"Sober up" the type checker by improving type inference over dictionary
literals that lack a contextual type. This fixes SR-305
1 parent 2351085 commit 2cdd7d6

File tree

2 files changed

+107
-15
lines changed

2 files changed

+107
-15
lines changed

lib/Sema/CSGen.cpp

Lines changed: 81 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,22 @@ static bool isArithmeticOperatorDecl(ValueDecl *vd) {
6969
vd->getName().str() == "%");
7070
}
7171

72+
static bool mergeRepresentativeEquivalenceClasses(ConstraintSystem &CS,
73+
TypeVariableType* tyvar1,
74+
TypeVariableType* tyvar2) {
75+
if (tyvar1 && tyvar2) {
76+
auto rep1 = CS.getRepresentative(tyvar1);
77+
auto rep2 = CS.getRepresentative(tyvar2);
78+
79+
if (rep1 != rep2) {
80+
CS.mergeEquivalenceClasses(rep1, rep2, /*updateWorkList*/ false);
81+
return true;
82+
}
83+
}
84+
85+
return false;
86+
}
87+
7288
namespace {
7389

7490
/// Internal struct for tracking information about types within a series
@@ -327,14 +343,7 @@ namespace {
327343
auto tyvar1 = acp1->getType()->getAs<TypeVariableType>();
328344
auto tyvar2 = acp2->getType()->getAs<TypeVariableType>();
329345

330-
if (tyvar1 && tyvar2) {
331-
auto rep1 = CS.getRepresentative(tyvar1);
332-
auto rep2 = CS.getRepresentative(tyvar2);
333-
334-
if (rep1 != rep2) {
335-
CS.mergeEquivalenceClasses(rep1, rep2, /*updateWorkList*/ false);
336-
}
337-
}
346+
mergeRepresentativeEquivalenceClasses(CS, tyvar1, tyvar2);
338347
}
339348
}
340349
}
@@ -1859,6 +1868,12 @@ namespace {
18591868
return arrayTy;
18601869
}
18611870

1871+
static bool isMergeableValueKind(Expr *expr) {
1872+
return isa<CollectionExpr>(expr) ||
1873+
isa<StringLiteralExpr>(expr) || isa<IntegerLiteralExpr>(expr) ||
1874+
isa<FloatLiteralExpr>(expr);
1875+
}
1876+
18621877
Type visitDictionaryExpr(DictionaryExpr *expr) {
18631878
ASTContext &C = CS.getASTContext();
18641879
// A dictionary expression can be of a type T that conforms to the
@@ -1908,16 +1923,67 @@ namespace {
19081923
TupleTypeElt(dictionaryValueTy) };
19091924
Type elementTy = TupleType::get(tupleElts, C);
19101925

1926+
// Keep track of which elements have been "merged". This way, we won't create
1927+
// needless conversion constraints for elements whose equivalence classes have
1928+
// been merged.
1929+
llvm::DenseSet<Expr *> mergedElements;
1930+
1931+
// If no contextual type is present, Merge equivalence classes of key
1932+
// and value types as necessary.
1933+
if (!CS.getContextualType(expr)) {
1934+
for (auto element1 : expr->getElements()) {
1935+
for (auto element2 : expr->getElements()) {
1936+
if (element1 == element2)
1937+
continue;
1938+
1939+
auto tty1 = element1->getType()->getAs<TupleType>();
1940+
auto tty2 = element2->getType()->getAs<TupleType>();
1941+
1942+
if (tty1 && tty2) {
1943+
auto mergedKey = false;
1944+
auto mergedValue = false;
1945+
1946+
auto keyTyvar1 = tty1->getElementTypes()[0]->
1947+
getAs<TypeVariableType>();
1948+
auto keyTyvar2 = tty2->getElementTypes()[0]->
1949+
getAs<TypeVariableType>();
1950+
1951+
mergedKey = mergeRepresentativeEquivalenceClasses(CS,
1952+
keyTyvar1, keyTyvar2);
1953+
1954+
auto valueTyvar1 = tty1->getElementTypes()[1]->
1955+
getAs<TypeVariableType>();
1956+
auto valueTyvar2 = tty2->getElementTypes()[1]->
1957+
getAs<TypeVariableType>();
1958+
1959+
auto elemExpr1 = dyn_cast<TupleExpr>(element1)->getElements()[1];
1960+
auto elemExpr2 = dyn_cast<TupleExpr>(element2)->getElements()[1];
1961+
1962+
if (elemExpr1->getKind() == elemExpr2->getKind() &&
1963+
isMergeableValueKind(elemExpr1)) {
1964+
mergedValue = mergeRepresentativeEquivalenceClasses(CS,
1965+
valueTyvar1, valueTyvar2);
1966+
}
1967+
1968+
if (mergedKey && mergedValue)
1969+
mergedElements.insert(element2);
1970+
}
1971+
}
1972+
}
1973+
}
1974+
19111975
// Introduce conversions from each element to the element type of the
1912-
// dictionary.
1976+
// dictionary. (If the equivalence class of an element has already been
1977+
// merged with a previous one, skip it.)
19131978
unsigned index = 0;
19141979
for (auto element : expr->getElements()) {
1915-
CS.addConstraint(ConstraintKind::Conversion,
1916-
element->getType(),
1917-
elementTy,
1918-
CS.getConstraintLocator(
1919-
expr,
1920-
LocatorPathElt::getTupleElement(index++)));
1980+
if (!mergedElements.count(element))
1981+
CS.addConstraint(ConstraintKind::Conversion,
1982+
element->getType(),
1983+
elementTy,
1984+
CS.getConstraintLocator(
1985+
expr,
1986+
LocatorPathElt::getTupleElement(index++)));
19211987
}
19221988

19231989
return dictionaryTy;
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// RUN: %target-parse-verify-swift
2+
3+
let dict = [
4+
"keys": [
5+
"key 1": ["key": "value"],
6+
"key 2": ["key": "value"],
7+
"key 3": ["key": "value"],
8+
"key 4": ["key": "value"],
9+
"key 5": ["key": "value"],
10+
"key 6": ["key": "value"],
11+
"key 7": ["key": "value"],
12+
"key 8": ["key": "value"],
13+
"key 9": ["key": "value"],
14+
"key 10": ["key": "value"],
15+
"key 11": ["key": "value"],
16+
"key 12": ["key": "value"],
17+
"key 13": ["key": "value"],
18+
"key 14": ["key": "value"],
19+
"key 15": ["key": "value"],
20+
"key 16": ["key": "value"],
21+
"key 17": ["key": "value"],
22+
"key 18": ["key": "value"],
23+
"key 19": ["key": "value"],
24+
"key 20": ["key": "value"],
25+
]
26+
]

0 commit comments

Comments
 (0)