Skip to content

KeyPaths: Add support for optional chaining/forcing components. #10556

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 26, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 70 additions & 9 deletions include/swift/SIL/SILInstruction.h
Original file line number Diff line number Diff line change
Expand Up @@ -1459,6 +1459,11 @@ class KeyPathPatternComponent {
StoredProperty,
GettableProperty,
SettableProperty,
Last_Packed = SettableProperty, // Last enum value that can be packed in
// a PointerIntPair
OptionalChain,
OptionalForce,
OptionalWrap,
};

// The pair of a captured index value and its Hashable conformance for a
Expand All @@ -1469,40 +1474,63 @@ class KeyPathPatternComponent {
};

private:
// Value is the VarDecl* for StoredProperty, and the SILFunction* of the
// Getter for computed properties
llvm::PointerIntPair<void *, 2, Kind> ValueAndKind;
static constexpr const unsigned KindPackingBits = 2;
static constexpr const unsigned UnpackedKind = (1u << KindPackingBits) - 1;
static_assert((unsigned)Kind::Last_Packed < UnpackedKind,
"too many kinds to pack");

// Value is the VarDecl* for StoredProperty, the SILFunction* of the
// Getter for computed properties, or the Kind for other kinds
llvm::PointerIntPair<void *, KindPackingBits, unsigned> ValueAndKind;
// false if id is a SILFunction*; true if id is a SILDeclRef
llvm::PointerIntPair<SILFunction *, 2, ComputedPropertyId::KindType>
llvm::PointerIntPair<SILFunction *, 2,
ComputedPropertyId::KindType>
SetterAndIdKind;
ComputedPropertyId::ValueType IdValue;
ArrayRef<IndexPair> Indices;
CanType ComponentType;


unsigned kindForPacking(Kind k) {
auto value = (unsigned)k;
assert(value <= (unsigned)Kind::Last_Packed);
return value;
}

KeyPathPatternComponent(Kind kind, CanType ComponentType)
: ValueAndKind((void*)((uintptr_t)kind << KindPackingBits), UnpackedKind),
ComponentType(ComponentType)
{
assert(kind > Kind::Last_Packed && "wrong initializer");
}

KeyPathPatternComponent(VarDecl *storedProp, Kind kind,
CanType ComponentType)
: ValueAndKind(storedProp, kind), ComponentType(ComponentType) {}
: ValueAndKind(storedProp, kindForPacking(kind)),
ComponentType(ComponentType) {}

KeyPathPatternComponent(ComputedPropertyId id, Kind kind,
SILFunction *getter,
SILFunction *setter,
ArrayRef<IndexPair> indices,
CanType ComponentType)
: ValueAndKind(getter, kind),
: ValueAndKind(getter, kindForPacking(kind)),
SetterAndIdKind(setter, id.Kind),
IdValue(id.Value),
Indices(indices),
ComponentType(ComponentType) {}

public:
KeyPathPatternComponent() : ValueAndKind(nullptr, (Kind)0) {}
KeyPathPatternComponent() : ValueAndKind(nullptr, 0) {}

bool isNull() const {
return ValueAndKind.getPointer() == nullptr;
}

Kind getKind() const {
return ValueAndKind.getInt();
auto packedKind = ValueAndKind.getInt();
if (packedKind != UnpackedKind)
return (Kind)packedKind;
return (Kind)((uintptr_t)ValueAndKind.getPointer() >> KindPackingBits);
}

CanType getComponentType() const {
Expand All @@ -1515,6 +1543,9 @@ class KeyPathPatternComponent {
return static_cast<VarDecl*>(ValueAndKind.getPointer());
case Kind::GettableProperty:
case Kind::SettableProperty:
case Kind::OptionalChain:
case Kind::OptionalForce:
case Kind::OptionalWrap:
llvm_unreachable("not a stored property");
}
llvm_unreachable("unhandled kind");
Expand All @@ -1523,6 +1554,9 @@ class KeyPathPatternComponent {
ComputedPropertyId getComputedPropertyId() const {
switch (getKind()) {
case Kind::StoredProperty:
case Kind::OptionalChain:
case Kind::OptionalForce:
case Kind::OptionalWrap:
llvm_unreachable("not a computed property");
case Kind::GettableProperty:
case Kind::SettableProperty:
Expand All @@ -1534,6 +1568,9 @@ class KeyPathPatternComponent {
SILFunction *getComputedPropertyGetter() const {
switch (getKind()) {
case Kind::StoredProperty:
case Kind::OptionalChain:
case Kind::OptionalForce:
case Kind::OptionalWrap:
llvm_unreachable("not a computed property");
case Kind::GettableProperty:
case Kind::SettableProperty:
Expand All @@ -1546,6 +1583,9 @@ class KeyPathPatternComponent {
switch (getKind()) {
case Kind::StoredProperty:
case Kind::GettableProperty:
case Kind::OptionalChain:
case Kind::OptionalForce:
case Kind::OptionalWrap:
llvm_unreachable("not a settable computed property");
case Kind::SettableProperty:
return SetterAndIdKind.getPointer();
Expand All @@ -1556,6 +1596,9 @@ class KeyPathPatternComponent {
ArrayRef<IndexPair> getComputedPropertyIndices() const {
switch (getKind()) {
case Kind::StoredProperty:
case Kind::OptionalChain:
case Kind::OptionalForce:
case Kind::OptionalWrap:
llvm_unreachable("not a computed property");
case Kind::GettableProperty:
case Kind::SettableProperty:
Expand Down Expand Up @@ -1589,6 +1632,24 @@ class KeyPathPatternComponent {
getter, setter, indices, ty);
}

static KeyPathPatternComponent
forOptional(Kind kind, CanType ty) {
switch (kind) {
case Kind::OptionalChain:
case Kind::OptionalForce:
break;
case Kind::OptionalWrap:
assert(ty->getAnyOptionalObjectType()
&& "optional wrap didn't form optional?!");
break;
case Kind::StoredProperty:
case Kind::GettableProperty:
case Kind::SettableProperty:
llvm_unreachable("not an optional kind");
}
return KeyPathPatternComponent(kind, ty);
}

void incrementRefCounts() const;
void decrementRefCounts() const;

Expand Down
10 changes: 10 additions & 0 deletions lib/IRGen/GenKeyPath.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -380,7 +380,17 @@ IRGenModule::getAddrOfKeyPathPattern(KeyPathPattern *pattern,
"generic computed key paths");
return llvm::UndefValue::get(Int8PtrTy);
}
break;
}
case KeyPathPatternComponent::Kind::OptionalChain:
fields.addInt32(KeyPathComponentHeader::forOptionalChain().getData());
break;
case KeyPathPatternComponent::Kind::OptionalForce:
fields.addInt32(KeyPathComponentHeader::forOptionalForce().getData());
break;
case KeyPathPatternComponent::Kind::OptionalWrap:
fields.addInt32(KeyPathComponentHeader::forOptionalWrap().getData());
break;
}

// For all but the last component, we pack in the type of the component.
Expand Down
22 changes: 22 additions & 0 deletions lib/Parse/ParseSIL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2472,6 +2472,7 @@ bool SILParser::parseSILInstruction(SILBasicBlock *BB, SILBuilder &B) {
if (!P.consumeIf(tok::comma))
break;
}

if ((idFn == nullptr && idDecl.isNull() && idProperty == nullptr)
|| getter == nullptr
|| (isSettable && setter == nullptr)) {
Expand Down Expand Up @@ -2509,6 +2510,27 @@ bool SILParser::parseSILInstruction(SILBasicBlock *BB, SILBuilder &B) {
KeyPathPatternComponent::forComputedGettableProperty(
id, getter, {}, componentTy));
}
} else if (componentKind.str() == "optional_wrap"
|| componentKind.str() == "optional_chain"
|| componentKind.str() == "optional_force") {
CanType ty;
if (P.parseToken(tok::colon, diag::expected_tok_in_sil_instr, ":")
|| P.parseToken(tok::sil_dollar, diag::expected_tok_in_sil_instr, "$")
|| parseASTType(ty, patternEnv))
return true;
KeyPathPatternComponent::Kind kind;

if (componentKind.str() == "optional_wrap") {
kind = KeyPathPatternComponent::Kind::OptionalWrap;
} else if (componentKind.str() == "optional_chain") {
kind = KeyPathPatternComponent::Kind::OptionalChain;
} else if (componentKind.str() == "optional_force") {
kind = KeyPathPatternComponent::Kind::OptionalForce;
} else {
llvm_unreachable("unpossible");
}

components.push_back(KeyPathPatternComponent::forOptional(kind, ty));
} else {
P.diagnose(componentLoc, diag::sil_keypath_unknown_component_kind,
componentKind);
Expand Down
14 changes: 14 additions & 0 deletions lib/SIL/SILInstructions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1983,6 +1983,9 @@ bool KeyPathPatternComponent::isComputedSettablePropertyMutating() const {
switch (getKind()) {
case Kind::StoredProperty:
case Kind::GettableProperty:
case Kind::OptionalChain:
case Kind::OptionalWrap:
case Kind::OptionalForce:
llvm_unreachable("not a settable computed property");
case Kind::SettableProperty: {
auto setter = getComputedPropertySetter();
Expand All @@ -1997,6 +2000,9 @@ forEachRefcountableReference(const KeyPathPatternComponent &component,
llvm::function_ref<void (SILFunction*)> forFunction) {
switch (component.getKind()) {
case KeyPathPatternComponent::Kind::StoredProperty:
case KeyPathPatternComponent::Kind::OptionalChain:
case KeyPathPatternComponent::Kind::OptionalWrap:
case KeyPathPatternComponent::Kind::OptionalForce:
return;
case KeyPathPatternComponent::Kind::SettableProperty:
forFunction(component.getComputedPropertySetter());
Expand Down Expand Up @@ -2043,6 +2049,9 @@ KeyPathPattern::get(SILModule &M, CanGenericSignature signature,
for (auto component : components) {
switch (component.getKind()) {
case KeyPathPatternComponent::Kind::StoredProperty:
case KeyPathPatternComponent::Kind::OptionalChain:
case KeyPathPatternComponent::Kind::OptionalWrap:
case KeyPathPatternComponent::Kind::OptionalForce:
break;

case KeyPathPatternComponent::Kind::GettableProperty:
Expand Down Expand Up @@ -2104,6 +2113,11 @@ void KeyPathPattern::Profile(llvm::FoldingSetNodeID &ID,
for (auto &component : components) {
ID.AddInteger((unsigned)component.getKind());
switch (component.getKind()) {
case KeyPathPatternComponent::Kind::OptionalForce:
case KeyPathPatternComponent::Kind::OptionalWrap:
case KeyPathPatternComponent::Kind::OptionalChain:
break;

case KeyPathPatternComponent::Kind::StoredProperty:
ID.AddPointer(component.getStoredPropertyDecl());
break;
Expand Down
19 changes: 19 additions & 0 deletions lib/SIL/SILPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1860,6 +1860,25 @@ class SILPrinter : public SILVisitor<SILPrinter> {
&& "todo");
break;
}
case KeyPathPatternComponent::Kind::OptionalWrap:
case KeyPathPatternComponent::Kind::OptionalChain:
case KeyPathPatternComponent::Kind::OptionalForce: {
switch (kind) {
case KeyPathPatternComponent::Kind::OptionalWrap:
*this << "optional_wrap : $";
break;
case KeyPathPatternComponent::Kind::OptionalChain:
*this << "optional_chain : $";
break;
case KeyPathPatternComponent::Kind::OptionalForce:
*this << "optional_force : $";
break;
default:
llvm_unreachable("out of sync");
}
*this << component.getComponentType();
break;
}
}
}

Expand Down
18 changes: 18 additions & 0 deletions lib/SIL/SILVerifier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3817,6 +3817,24 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {

break;
}
case KeyPathPatternComponent::Kind::OptionalChain: {
require(OptionalType::get(componentTy)->isEqual(baseTy),
"chaining component should unwrap optional");
require(leafTy->getAnyOptionalObjectType(),
"key path with chaining component should have optional "
"result");
break;
}
case KeyPathPatternComponent::Kind::OptionalForce: {
require(OptionalType::get(componentTy)->isEqual(baseTy),
"forcing component should unwrap optional");
break;
}
case KeyPathPatternComponent::Kind::OptionalWrap: {
require(OptionalType::get(baseTy)->isEqual(componentTy),
"wrapping component should wrap optional");
break;
}
}

baseTy = componentTy;
Expand Down
32 changes: 27 additions & 5 deletions lib/SILGen/SILGenExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2793,7 +2793,7 @@ RValue RValueEmitter::visitKeyPathExpr(KeyPathExpr *E, SGFContext C) {
auto baseTy = rootTy;

for (auto &component : E->getComponents()) {
switch (component.getKind()) {
switch (auto kind = component.getKind()) {
case KeyPathExpr::Component::Kind::Property: {
auto decl = cast<VarDecl>(component.getDeclRef().getDecl());
auto oldBaseTy = baseTy;
Expand Down Expand Up @@ -2855,12 +2855,34 @@ RValue RValueEmitter::visitKeyPathExpr(KeyPathExpr *E, SGFContext C) {
break;
}

case KeyPathExpr::Component::Kind::Subscript:
case KeyPathExpr::Component::Kind::OptionalChain:
case KeyPathExpr::Component::Kind::OptionalForce:
case KeyPathExpr::Component::Kind::OptionalWrap:
return unsupported("non-property key path component");

case KeyPathExpr::Component::Kind::OptionalWrap: {
KeyPathPatternComponent::Kind loweredKind;
switch (kind) {
case KeyPathExpr::Component::Kind::OptionalChain:
loweredKind = KeyPathPatternComponent::Kind::OptionalChain;
baseTy = baseTy->getAnyOptionalObjectType()->getCanonicalType();
break;
case KeyPathExpr::Component::Kind::OptionalForce:
loweredKind = KeyPathPatternComponent::Kind::OptionalForce;
baseTy = baseTy->getAnyOptionalObjectType()->getCanonicalType();
break;
case KeyPathExpr::Component::Kind::OptionalWrap:
loweredKind = KeyPathPatternComponent::Kind::OptionalWrap;
baseTy = OptionalType::get(baseTy)->getCanonicalType();
break;
default:
llvm_unreachable("out of sync");
}
loweredComponents.push_back(
KeyPathPatternComponent::forOptional(loweredKind, baseTy));
break;
}

case KeyPathExpr::Component::Kind::Subscript:
return unsupported("subscript key path component");

case KeyPathExpr::Component::Kind::Invalid:
case KeyPathExpr::Component::Kind::UnresolvedProperty:
case KeyPathExpr::Component::Kind::UnresolvedSubscript:
Expand Down
3 changes: 3 additions & 0 deletions lib/SILOptimizer/IPO/DeadFunctionElimination.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,9 @@ class FunctionLivenessComputation {
continue;
}
case KeyPathPatternComponent::Kind::StoredProperty:
case KeyPathPatternComponent::Kind::OptionalChain:
case KeyPathPatternComponent::Kind::OptionalForce:
case KeyPathPatternComponent::Kind::OptionalWrap:
continue;
}
}
Expand Down
3 changes: 2 additions & 1 deletion lib/Sema/CSApply.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4071,7 +4071,8 @@ namespace {
baseTy &&
!baseTy->hasUnresolvedType() &&
!baseTy->isEqual(leafTy)) {
assert(leafTy->getAnyOptionalObjectType()->isEqual(baseTy));
assert(leafTy->getAnyOptionalObjectType()
->isEqual(baseTy->getLValueOrInOutObjectType()));
auto component = KeyPathExpr::Component::forOptionalWrap(leafTy);
resolvedComponents.push_back(component);
baseTy = leafTy;
Expand Down
8 changes: 1 addition & 7 deletions lib/Sema/CSGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2848,15 +2848,9 @@ namespace {
case KeyPathExpr::Component::Kind::OptionalChain: {
didOptionalChain = true;

// TODO: This currently crashes the compiler in some cases, so short-
// circuit out.
if (!CS.TC.Context.LangOpts.EnableExperimentalKeyPathComponents) {
return ErrorType::get(CS.TC.Context);
}

// We can't assign an optional back through an optional chain
// today. Force the base to an rvalue.
auto rvalueTy = CS.createTypeVariable(locator, TVO_CanBindToInOut);
auto rvalueTy = CS.createTypeVariable(locator, 0);
CS.addConstraint(ConstraintKind::Equal, base, rvalueTy, locator);
base = rvalueTy;
LLVM_FALLTHROUGH;
Expand Down
Loading