mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
Sema: Infer the specific type of a key path literal from the mutability of the resolved components.
This commit is contained in:
@@ -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(),
|
||||
|
||||
Reference in New Issue
Block a user