"Sober up" the type checker by improving type inference over dictionary

literals that lack a contextual type.

This fixes SR-305
This commit is contained in:
Joe Pamer
2016-04-26 17:09:30 -07:00
parent 2351085e76
commit 2cdd7d64e1
2 changed files with 107 additions and 15 deletions

View File

@@ -69,6 +69,22 @@ static bool isArithmeticOperatorDecl(ValueDecl *vd) {
vd->getName().str() == "%"); vd->getName().str() == "%");
} }
static bool mergeRepresentativeEquivalenceClasses(ConstraintSystem &CS,
TypeVariableType* tyvar1,
TypeVariableType* tyvar2) {
if (tyvar1 && tyvar2) {
auto rep1 = CS.getRepresentative(tyvar1);
auto rep2 = CS.getRepresentative(tyvar2);
if (rep1 != rep2) {
CS.mergeEquivalenceClasses(rep1, rep2, /*updateWorkList*/ false);
return true;
}
}
return false;
}
namespace { namespace {
/// Internal struct for tracking information about types within a series /// Internal struct for tracking information about types within a series
@@ -327,14 +343,7 @@ namespace {
auto tyvar1 = acp1->getType()->getAs<TypeVariableType>(); auto tyvar1 = acp1->getType()->getAs<TypeVariableType>();
auto tyvar2 = acp2->getType()->getAs<TypeVariableType>(); auto tyvar2 = acp2->getType()->getAs<TypeVariableType>();
if (tyvar1 && tyvar2) { mergeRepresentativeEquivalenceClasses(CS, tyvar1, tyvar2);
auto rep1 = CS.getRepresentative(tyvar1);
auto rep2 = CS.getRepresentative(tyvar2);
if (rep1 != rep2) {
CS.mergeEquivalenceClasses(rep1, rep2, /*updateWorkList*/ false);
}
}
} }
} }
} }
@@ -1859,6 +1868,12 @@ namespace {
return arrayTy; return arrayTy;
} }
static bool isMergeableValueKind(Expr *expr) {
return isa<CollectionExpr>(expr) ||
isa<StringLiteralExpr>(expr) || isa<IntegerLiteralExpr>(expr) ||
isa<FloatLiteralExpr>(expr);
}
Type visitDictionaryExpr(DictionaryExpr *expr) { Type visitDictionaryExpr(DictionaryExpr *expr) {
ASTContext &C = CS.getASTContext(); ASTContext &C = CS.getASTContext();
// A dictionary expression can be of a type T that conforms to the // A dictionary expression can be of a type T that conforms to the
@@ -1908,16 +1923,67 @@ namespace {
TupleTypeElt(dictionaryValueTy) }; TupleTypeElt(dictionaryValueTy) };
Type elementTy = TupleType::get(tupleElts, C); Type elementTy = TupleType::get(tupleElts, C);
// Keep track of which elements have been "merged". This way, we won't create
// needless conversion constraints for elements whose equivalence classes have
// been merged.
llvm::DenseSet<Expr *> mergedElements;
// If no contextual type is present, Merge equivalence classes of key
// and value types as necessary.
if (!CS.getContextualType(expr)) {
for (auto element1 : expr->getElements()) {
for (auto element2 : expr->getElements()) {
if (element1 == element2)
continue;
auto tty1 = element1->getType()->getAs<TupleType>();
auto tty2 = element2->getType()->getAs<TupleType>();
if (tty1 && tty2) {
auto mergedKey = false;
auto mergedValue = false;
auto keyTyvar1 = tty1->getElementTypes()[0]->
getAs<TypeVariableType>();
auto keyTyvar2 = tty2->getElementTypes()[0]->
getAs<TypeVariableType>();
mergedKey = mergeRepresentativeEquivalenceClasses(CS,
keyTyvar1, keyTyvar2);
auto valueTyvar1 = tty1->getElementTypes()[1]->
getAs<TypeVariableType>();
auto valueTyvar2 = tty2->getElementTypes()[1]->
getAs<TypeVariableType>();
auto elemExpr1 = dyn_cast<TupleExpr>(element1)->getElements()[1];
auto elemExpr2 = dyn_cast<TupleExpr>(element2)->getElements()[1];
if (elemExpr1->getKind() == elemExpr2->getKind() &&
isMergeableValueKind(elemExpr1)) {
mergedValue = mergeRepresentativeEquivalenceClasses(CS,
valueTyvar1, valueTyvar2);
}
if (mergedKey && mergedValue)
mergedElements.insert(element2);
}
}
}
}
// Introduce conversions from each element to the element type of the // Introduce conversions from each element to the element type of the
// dictionary. // dictionary. (If the equivalence class of an element has already been
// merged with a previous one, skip it.)
unsigned index = 0; unsigned index = 0;
for (auto element : expr->getElements()) { for (auto element : expr->getElements()) {
CS.addConstraint(ConstraintKind::Conversion, if (!mergedElements.count(element))
element->getType(), CS.addConstraint(ConstraintKind::Conversion,
elementTy, element->getType(),
CS.getConstraintLocator( elementTy,
expr, CS.getConstraintLocator(
LocatorPathElt::getTupleElement(index++))); expr,
LocatorPathElt::getTupleElement(index++)));
} }
return dictionaryTy; return dictionaryTy;

View File

@@ -0,0 +1,26 @@
// RUN: %target-parse-verify-swift
let dict = [
"keys": [
"key 1": ["key": "value"],
"key 2": ["key": "value"],
"key 3": ["key": "value"],
"key 4": ["key": "value"],
"key 5": ["key": "value"],
"key 6": ["key": "value"],
"key 7": ["key": "value"],
"key 8": ["key": "value"],
"key 9": ["key": "value"],
"key 10": ["key": "value"],
"key 11": ["key": "value"],
"key 12": ["key": "value"],
"key 13": ["key": "value"],
"key 14": ["key": "value"],
"key 15": ["key": "value"],
"key 16": ["key": "value"],
"key 17": ["key": "value"],
"key 18": ["key": "value"],
"key 19": ["key": "value"],
"key 20": ["key": "value"],
]
]