Sema: Infer the specific type of a key path literal from the mutability of the resolved components.

This commit is contained in:
Joe Groff
2017-04-10 16:06:40 -07:00
parent 39a0849362
commit 7eebb27153
9 changed files with 350 additions and 45 deletions

View File

@@ -713,6 +713,7 @@ matchCallArguments(ConstraintSystem &cs, ConstraintKind kind,
case ConstraintKind::DynamicTypeOf:
case ConstraintKind::EscapableFunctionOf:
case ConstraintKind::OpenedExistentialOf:
case ConstraintKind::KeyPath:
case ConstraintKind::KeyPathApplication:
case ConstraintKind::LiteralConformsTo:
case ConstraintKind::OptionalObject:
@@ -840,6 +841,7 @@ ConstraintSystem::matchTupleTypes(TupleType *tuple1, TupleType *tuple2,
case ConstraintKind::DynamicTypeOf:
case ConstraintKind::EscapableFunctionOf:
case ConstraintKind::OpenedExistentialOf:
case ConstraintKind::KeyPath:
case ConstraintKind::KeyPathApplication:
case ConstraintKind::LiteralConformsTo:
case ConstraintKind::OptionalObject:
@@ -957,6 +959,7 @@ static bool matchFunctionRepresentations(FunctionTypeRepresentation rep1,
case ConstraintKind::DynamicTypeOf:
case ConstraintKind::EscapableFunctionOf:
case ConstraintKind::OpenedExistentialOf:
case ConstraintKind::KeyPath:
case ConstraintKind::KeyPathApplication:
case ConstraintKind::LiteralConformsTo:
case ConstraintKind::OptionalObject:
@@ -1027,6 +1030,7 @@ ConstraintSystem::matchFunctionTypes(FunctionType *func1, FunctionType *func2,
case ConstraintKind::DynamicTypeOf:
case ConstraintKind::EscapableFunctionOf:
case ConstraintKind::OpenedExistentialOf:
case ConstraintKind::KeyPath:
case ConstraintKind::KeyPathApplication:
case ConstraintKind::LiteralConformsTo:
case ConstraintKind::OptionalObject:
@@ -1555,6 +1559,7 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind,
case ConstraintKind::DynamicTypeOf:
case ConstraintKind::EscapableFunctionOf:
case ConstraintKind::OpenedExistentialOf:
case ConstraintKind::KeyPath:
case ConstraintKind::KeyPathApplication:
case ConstraintKind::LiteralConformsTo:
case ConstraintKind::OptionalObject:
@@ -3179,7 +3184,6 @@ retry_after_fail:
return result;
}
ConstraintSystem::SolutionKind
ConstraintSystem::simplifyMemberConstraint(ConstraintKind kind,
Type baseTy, DeclName member,
@@ -3685,6 +3689,148 @@ ConstraintSystem::simplifyOpenedExistentialOfConstraint(
return SolutionKind::Unsolved;
}
ConstraintSystem::SolutionKind
ConstraintSystem::simplifyKeyPathConstraint(Type keyPathTy,
Type rootTy,
Type valueTy,
TypeMatchOptions flags,
ConstraintLocatorBuilder locator) {
auto subflags = getDefaultDecompositionOptions(flags);
// The constraint ought to have been anchored on a KeyPathExpr.
auto keyPath = cast<KeyPathExpr>(locator.getBaseLocator()->getAnchor());
// Gather overload choices for any key path components associated with this
// key path.
SmallVector<OverloadChoice, 4> choices;
choices.resize(keyPath->getComponents().size());
for (auto resolvedItem = resolvedOverloadSets; resolvedItem;
resolvedItem = resolvedItem->Previous) {
auto locator = resolvedItem->Locator;
if (locator->getAnchor() == keyPath
&& locator->getPath().size() == 1
&& locator->getPath()[0].getKind() == ConstraintLocator::KeyPathComponent) {
choices[locator->getPath()[0].getValue()] = resolvedItem->Choice;
}
}
keyPathTy = getFixedTypeRecursive(keyPathTy, /*want rvalue*/ true);
auto tryMatchRootAndValueFromKeyPathType =
[&](BoundGenericType *bgt) -> SolutionKind {
Type boundRoot, boundValue;
// We can get root and value from a concrete key path type.
if (bgt->getDecl() == getASTContext().getKeyPathDecl()
|| bgt->getDecl() == getASTContext().getWritableKeyPathDecl()
|| bgt->getDecl() == getASTContext().getReferenceWritableKeyPathDecl()) {
boundRoot = bgt->getGenericArgs()[0];
boundValue = bgt->getGenericArgs()[1];
} else if (bgt->getDecl() == getASTContext().getPartialKeyPathDecl()) {
// We can still get the root from a PartialKeyPath.
boundRoot = bgt->getGenericArgs()[0];
boundValue = Type();
} else {
// We can't bind anything from this type.
return SolutionKind::Solved;
}
if (matchTypes(boundRoot, rootTy,
ConstraintKind::Bind, subflags, locator) == SolutionKind::Error)
return SolutionKind::Error;
if (boundValue
&& matchTypes(boundValue, valueTy,
ConstraintKind::Bind, subflags, locator) == SolutionKind::Error)
return SolutionKind::Error;
return SolutionKind::Solved;
};
// If the key path type is bound from context, feed that into the root and
// value types.
if (auto keyPathBGT = keyPathTy->getAs<BoundGenericType>()) {
if (tryMatchRootAndValueFromKeyPathType(keyPathBGT) == SolutionKind::Error)
return SolutionKind::Error;
}
// See if we resolved overloads for all the components involved.
enum {
ReadOnly,
Writable,
ReferenceWritable
} capability = Writable;
for (unsigned i : indices(keyPath->getComponents())) {
auto &component = keyPath->getComponents()[i];
switch (component.getKind()) {
case KeyPathExpr::Component::Kind::UnresolvedProperty:
case KeyPathExpr::Component::Kind::UnresolvedSubscript: {
// If no choice was made, leave the constraint unsolved.
if (choices[i].isInvalid()) {
if (flags.contains(TMF_GenerateConstraints)) {
addUnsolvedConstraint(Constraint::create(*this,
ConstraintKind::KeyPath,
keyPathTy, rootTy, valueTy,
locator.getBaseLocator()));
return SolutionKind::Solved;
}
return SolutionKind::Unsolved;
}
auto storage = cast<AbstractStorageDecl>(choices[i].getDecl());
if (!storage->isSettable(DC)) {
// A non-settable component makes the key path read-only, unless
// a reference-writable component shows up later.
capability = ReadOnly;
continue;
}
// A nonmutating setter indicates a reference-writable base.
if (storage->isSetterNonMutating()) {
capability = ReferenceWritable;
continue;
}
// Otherwise, the key path maintains its current capability.
break;
}
case KeyPathExpr::Component::Kind::OptionalChain:
// Optional chains force the entire key path to be read-only.
capability = ReadOnly;
goto done;
case KeyPathExpr::Component::Kind::OptionalForce:
// Forcing an optional preserves its lvalue-ness.
break;
case KeyPathExpr::Component::Kind::Property:
case KeyPathExpr::Component::Kind::Subscript:
case KeyPathExpr::Component::Kind::OptionalWrap:
llvm_unreachable("already resolved");
}
}
done:
// Resolve the type.
NominalTypeDecl *kpDecl;
switch (capability) {
case ReadOnly:
kpDecl = getASTContext().getKeyPathDecl();
break;
case Writable:
kpDecl = getASTContext().getWritableKeyPathDecl();
break;
case ReferenceWritable:
kpDecl = getASTContext().getReferenceWritableKeyPathDecl();
break;
}
auto resolvedKPTy = BoundGenericType::get(kpDecl, nullptr,
{rootTy, valueTy});
return matchTypes(resolvedKPTy, keyPathTy, ConstraintKind::Subtype,
subflags, locator);
}
ConstraintSystem::SolutionKind
ConstraintSystem::simplifyKeyPathApplicationConstraint(
Type keyPathTy,
@@ -3697,7 +3843,8 @@ ConstraintSystem::simplifyKeyPathApplicationConstraint(
auto unsolved = [&]() -> SolutionKind {
if (flags.contains(TMF_GenerateConstraints)) {
addUnsolvedConstraint(Constraint::createKeyPathApplication(*this,
addUnsolvedConstraint(Constraint::create(*this,
ConstraintKind::KeyPathApplication,
keyPathTy, rootTy, valueTy, getConstraintLocator(locator)));
return SolutionKind::Solved;
}
@@ -4452,6 +4599,7 @@ ConstraintSystem::addConstraintImpl(ConstraintKind kind, Type first,
case ConstraintKind::UnresolvedValueMember:
case ConstraintKind::BindOverload:
case ConstraintKind::Disjunction:
case ConstraintKind::KeyPath:
case ConstraintKind::KeyPathApplication:
llvm_unreachable("Use the correct addConstraint()");
}
@@ -4469,8 +4617,9 @@ ConstraintSystem::addKeyPathApplicationConstraint(Type keypath,
locator)) {
case SolutionKind::Error:
if (shouldAddNewFailingConstraint()) {
auto c = Constraint::createKeyPathApplication(*this, keypath, root, value,
getConstraintLocator(locator));
auto c = Constraint::create(*this, ConstraintKind::KeyPathApplication,
keypath, root, value,
getConstraintLocator(locator));
if (isFavored) c->setFavored();
addNewFailingConstraint(c);
}
@@ -4484,6 +4633,33 @@ ConstraintSystem::addKeyPathApplicationConstraint(Type keypath,
}
}
void
ConstraintSystem::addKeyPathConstraint(Type keypath,
Type root, Type value,
ConstraintLocatorBuilder locator,
bool isFavored) {
switch (simplifyKeyPathConstraint(keypath, root, value,
TMF_GenerateConstraints,
locator)) {
case SolutionKind::Error:
if (shouldAddNewFailingConstraint()) {
auto c = Constraint::create(*this, ConstraintKind::KeyPath,
keypath, root, value,
getConstraintLocator(locator));
if (isFavored) c->setFavored();
addNewFailingConstraint(c);
}
return;
case SolutionKind::Solved:
return;
case SolutionKind::Unsolved:
llvm_unreachable("should have generated constraints");
}
}
void ConstraintSystem::addConstraint(ConstraintKind kind, Type first,
Type second,
ConstraintLocatorBuilder locator,
@@ -4609,6 +4785,12 @@ ConstraintSystem::simplifyConstraint(const Constraint &constraint) {
None,
constraint.getLocator());
case ConstraintKind::KeyPath:
return simplifyKeyPathConstraint(
constraint.getFirstType(), constraint.getSecondType(),
constraint.getThirdType(),
None, constraint.getLocator());
case ConstraintKind::KeyPathApplication:
return simplifyKeyPathApplicationConstraint(
constraint.getFirstType(), constraint.getSecondType(),