mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
SILGen: Handle existential keypath root types.
SR-4917|rdar://problem/32254554
This commit is contained in:
@@ -589,6 +589,9 @@ public:
|
|||||||
/// bound.
|
/// bound.
|
||||||
bool isClassExistentialType();
|
bool isClassExistentialType();
|
||||||
|
|
||||||
|
/// Opens an existential instance or meta-type and returns the opened type.
|
||||||
|
Type openAnyExistentialType(ArchetypeType *&opened);
|
||||||
|
|
||||||
/// Break an existential down into a set of constraints.
|
/// Break an existential down into a set of constraints.
|
||||||
ExistentialLayout getExistentialLayout();
|
ExistentialLayout getExistentialLayout();
|
||||||
|
|
||||||
|
|||||||
@@ -4146,3 +4146,16 @@ getRecursivePropertiesFromSubstitutions(SubstitutionList Params) {
|
|||||||
}
|
}
|
||||||
return props;
|
return props;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Type TypeBase::openAnyExistentialType(ArchetypeType *&opened) {
|
||||||
|
assert(isAnyExistentialType());
|
||||||
|
if (auto metaty = getAs<ExistentialMetatypeType>()) {
|
||||||
|
opened = ArchetypeType::getOpened(metaty->getInstanceType());
|
||||||
|
if (metaty->hasRepresentation())
|
||||||
|
return MetatypeType::get(opened, metaty->getRepresentation());
|
||||||
|
else
|
||||||
|
return MetatypeType::get(opened);
|
||||||
|
}
|
||||||
|
opened = ArchetypeType::getOpened(this);
|
||||||
|
return opened;
|
||||||
|
}
|
||||||
|
|||||||
@@ -2415,6 +2415,44 @@ RValue RValueEmitter::visitObjCSelectorExpr(ObjCSelectorExpr *e, SGFContext C) {
|
|||||||
return RValue(SGF, e, ManagedValue::forUnmanaged(selectorValue));
|
return RValue(SGF, e, ManagedValue::forUnmanaged(selectorValue));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static ManagedValue
|
||||||
|
emitKeyPathRValueBase(SILGenFunction &subSGF,
|
||||||
|
VarDecl *property,
|
||||||
|
SILLocation loc,
|
||||||
|
SILValue paramArg,
|
||||||
|
CanType &baseType) {
|
||||||
|
auto paramOrigValue = subSGF.emitManagedRValueWithCleanup(paramArg);
|
||||||
|
auto paramSubstValue = subSGF.emitOrigToSubstValue(loc, paramOrigValue,
|
||||||
|
AbstractionPattern::getOpaque(),
|
||||||
|
baseType);
|
||||||
|
|
||||||
|
// Upcast a class instance to the property's declared type if necessary.
|
||||||
|
if (auto propertyClass = dyn_cast<ClassDecl>(property->getDeclContext())) {
|
||||||
|
if (baseType->getClassOrBoundGenericClass() != propertyClass) {
|
||||||
|
baseType = baseType->getSuperclassForDecl(propertyClass)
|
||||||
|
->getCanonicalType();
|
||||||
|
paramSubstValue = subSGF.B.createUpcast(loc, paramSubstValue,
|
||||||
|
SILType::getPrimitiveObjectType(baseType));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// …or pop open an existential container.
|
||||||
|
else if (baseType->isAnyExistentialType()) {
|
||||||
|
ArchetypeType *opened;
|
||||||
|
baseType = baseType->openAnyExistentialType(opened)->getCanonicalType();
|
||||||
|
auto openedOpaqueValue = subSGF.emitOpenExistential(loc, paramSubstValue,
|
||||||
|
opened, subSGF.SGM.getLoweredType(baseType),
|
||||||
|
AccessKind::Read);
|
||||||
|
// Maybe we could peephole this if we know the property load can borrow the
|
||||||
|
// base value…
|
||||||
|
if (!openedOpaqueValue.IsConsumable) {
|
||||||
|
paramSubstValue = openedOpaqueValue.Value.copyUnmanaged(subSGF, loc);
|
||||||
|
} else {
|
||||||
|
paramSubstValue = openedOpaqueValue.Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return paramSubstValue;
|
||||||
|
}
|
||||||
|
|
||||||
static SILFunction *getOrCreateKeyPathGetter(SILGenFunction &SGF,
|
static SILFunction *getOrCreateKeyPathGetter(SILGenFunction &SGF,
|
||||||
SILLocation loc,
|
SILLocation loc,
|
||||||
VarDecl *property,
|
VarDecl *property,
|
||||||
@@ -2482,22 +2520,10 @@ static SILFunction *getOrCreateKeyPathGetter(SILGenFunction &SGF,
|
|||||||
|
|
||||||
Scope scope(subSGF, loc);
|
Scope scope(subSGF, loc);
|
||||||
|
|
||||||
auto paramOrigValue = subSGF.emitManagedRValueWithCleanup(paramArg);
|
auto paramSubstValue = emitKeyPathRValueBase(subSGF, property,
|
||||||
auto paramSubstValue = subSGF.emitOrigToSubstValue(loc, paramOrigValue,
|
loc, paramArg,
|
||||||
AbstractionPattern::getOpaque(),
|
baseType);
|
||||||
baseType);
|
|
||||||
|
|
||||||
// Upcast a class instance to the property's declared type if necessary.
|
|
||||||
if (auto propertyClass = dyn_cast<ClassDecl>(property->getDeclContext())) {
|
|
||||||
if (baseType->getClassOrBoundGenericClass() != propertyClass) {
|
|
||||||
do {
|
|
||||||
baseType = baseType->getSuperclass()->getCanonicalType();
|
|
||||||
} while (baseType->getClassOrBoundGenericClass() != propertyClass);
|
|
||||||
paramSubstValue = subSGF.B.createUpcast(loc, paramSubstValue,
|
|
||||||
SILType::getPrimitiveObjectType(baseType));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
auto subs = baseType->getContextSubstitutionMap(subSGF.SGM.M.getSwiftModule(),
|
auto subs = baseType->getContextSubstitutionMap(subSGF.SGM.M.getSwiftModule(),
|
||||||
property->getInnermostDeclContext()->getInnermostTypeContext());
|
property->getInnermostDeclContext()->getInnermostTypeContext());
|
||||||
SmallVector<Substitution, 4> subsList;
|
SmallVector<Substitution, 4> subsList;
|
||||||
@@ -2602,20 +2628,9 @@ SILFunction *getOrCreateKeyPathSetter(SILGenFunction &SGF,
|
|||||||
|
|
||||||
LValue lv;
|
LValue lv;
|
||||||
if (property->isSetterNonMutating()) {
|
if (property->isSetterNonMutating()) {
|
||||||
auto baseOrig = subSGF.emitManagedRValueWithCleanup(baseArg);
|
auto baseSubst = emitKeyPathRValueBase(subSGF, property,
|
||||||
auto baseSubst = subSGF.emitOrigToSubstValue(loc, baseOrig,
|
loc, baseArg,
|
||||||
AbstractionPattern::getOpaque(),
|
baseType);
|
||||||
baseType);
|
|
||||||
// Upcast a class instance to the property's declared type if necessary.
|
|
||||||
if (auto propertyClass = dyn_cast<ClassDecl>(property->getDeclContext())) {
|
|
||||||
if (baseType->getClassOrBoundGenericClass() != propertyClass) {
|
|
||||||
do {
|
|
||||||
baseType = baseType->getSuperclass()->getCanonicalType();
|
|
||||||
} while (baseType->getClassOrBoundGenericClass() != propertyClass);
|
|
||||||
baseSubst = subSGF.B.createUpcast(loc, baseSubst,
|
|
||||||
SILType::getPrimitiveObjectType(baseType));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
lv = LValue::forValue(baseSubst, baseType);
|
lv = LValue::forValue(baseSubst, baseType);
|
||||||
} else {
|
} else {
|
||||||
@@ -2623,6 +2638,16 @@ SILFunction *getOrCreateKeyPathSetter(SILGenFunction &SGF,
|
|||||||
lv = LValue::forAddress(baseOrig, None,
|
lv = LValue::forAddress(baseOrig, None,
|
||||||
AbstractionPattern::getOpaque(),
|
AbstractionPattern::getOpaque(),
|
||||||
baseType);
|
baseType);
|
||||||
|
|
||||||
|
// Open an existential lvalue, if necessary.
|
||||||
|
if (baseType->isAnyExistentialType()) {
|
||||||
|
ArchetypeType *opened;
|
||||||
|
baseType = baseType->openAnyExistentialType(opened)->getCanonicalType();
|
||||||
|
lv = subSGF.emitOpenExistentialLValue(loc, std::move(lv),
|
||||||
|
CanArchetypeType(opened),
|
||||||
|
baseType,
|
||||||
|
AccessKind::ReadWrite);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto subs = baseType->getContextSubstitutionMap(subSGF.SGM.M.getSwiftModule(),
|
auto subs = baseType->getContextSubstitutionMap(subSGF.SGM.M.getSwiftModule(),
|
||||||
|
|||||||
@@ -1751,13 +1751,20 @@ namespace {
|
|||||||
|
|
||||||
LValue LValue::forValue(ManagedValue value,
|
LValue LValue::forValue(ManagedValue value,
|
||||||
CanType substFormalType) {
|
CanType substFormalType) {
|
||||||
assert(value.getType().isObject());
|
if (value.getType().isObject()) {
|
||||||
LValueTypeData typeData = getValueTypeData(substFormalType,
|
LValueTypeData typeData = getValueTypeData(substFormalType,
|
||||||
value.getValue());
|
value.getValue());
|
||||||
|
|
||||||
LValue lv;
|
LValue lv;
|
||||||
lv.add<ValueComponent>(value, None, typeData, /*isRValue=*/true);
|
lv.add<ValueComponent>(value, None, typeData, /*isRValue=*/true);
|
||||||
return lv;
|
return lv;
|
||||||
|
} else {
|
||||||
|
// Treat an address-only value as an lvalue we only read from.
|
||||||
|
if (!value.isLValue())
|
||||||
|
value = ManagedValue::forLValue(value.getValue());
|
||||||
|
return forAddress(value, None, AbstractionPattern(substFormalType),
|
||||||
|
substFormalType);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LValue LValue::forAddress(ManagedValue address,
|
LValue LValue::forAddress(ManagedValue address,
|
||||||
|
|||||||
@@ -431,13 +431,7 @@ public:
|
|||||||
// Metatypes and bases of non-mutating setters on value types
|
// Metatypes and bases of non-mutating setters on value types
|
||||||
// are always rvalues.
|
// are always rvalues.
|
||||||
if (!SubstSelfType->getRValueInstanceType()->mayHaveSuperclass()) {
|
if (!SubstSelfType->getRValueInstanceType()->mayHaveSuperclass()) {
|
||||||
if (self.getType().isObject())
|
return LValue::forValue(self, SubstSelfType);
|
||||||
return LValue::forValue(self, SubstSelfType);
|
|
||||||
else {
|
|
||||||
if (!self.isLValue())
|
|
||||||
self = ManagedValue::forLValue(self.getValue());
|
|
||||||
return LValue::forAddress(self, None, selfPattern, SubstSelfType);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CanType witnessSelfType =
|
CanType witnessSelfType =
|
||||||
|
|||||||
@@ -31,6 +31,10 @@ extension P {
|
|||||||
var z: String {
|
var z: String {
|
||||||
return y
|
return y
|
||||||
}
|
}
|
||||||
|
var w: String {
|
||||||
|
get { return "" }
|
||||||
|
nonmutating set { }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// CHECK-LABEL: sil hidden @{{.*}}storedProperties
|
// CHECK-LABEL: sil hidden @{{.*}}storedProperties
|
||||||
@@ -171,3 +175,10 @@ class BB<U, V>: AA<V> {
|
|||||||
func keyPathForInheritedMember() {
|
func keyPathForInheritedMember() {
|
||||||
_ = \BB<Int, String>.a
|
_ = \BB<Int, String>.a
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func keyPathForExistentialMember() {
|
||||||
|
_ = \P.x
|
||||||
|
_ = \P.y
|
||||||
|
_ = \P.z
|
||||||
|
_ = \P.w
|
||||||
|
}
|
||||||
|
|||||||
@@ -279,12 +279,18 @@ keyPath.test("computed properties") {
|
|||||||
|
|
||||||
class AB {
|
class AB {
|
||||||
}
|
}
|
||||||
class ABC: AB {
|
class ABC: AB, ABCProtocol {
|
||||||
var a = LifetimeTracked(1)
|
var a = LifetimeTracked(1)
|
||||||
var b = LifetimeTracked(2)
|
var b = LifetimeTracked(2)
|
||||||
var c = LifetimeTracked(3)
|
var c = LifetimeTracked(3)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protocol ABCProtocol {
|
||||||
|
var a: LifetimeTracked { get }
|
||||||
|
var b: LifetimeTracked { get set }
|
||||||
|
var c: LifetimeTracked { get nonmutating set }
|
||||||
|
}
|
||||||
|
|
||||||
keyPath.test("dynamically-typed application") {
|
keyPath.test("dynamically-typed application") {
|
||||||
let cPaths = [\ABC.a, \ABC.b, \ABC.c]
|
let cPaths = [\ABC.a, \ABC.b, \ABC.c]
|
||||||
|
|
||||||
@@ -312,6 +318,32 @@ keyPath.test("dynamically-typed application") {
|
|||||||
expectTrue(wrongFields[1] == nil)
|
expectTrue(wrongFields[1] == nil)
|
||||||
expectTrue(wrongFields[2] == nil)
|
expectTrue(wrongFields[2] == nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var protoErasedSubject: ABCProtocol = subject
|
||||||
|
let protoErasedPathA = \ABCProtocol.a
|
||||||
|
let protoErasedPathB = \ABCProtocol.b
|
||||||
|
let protoErasedPathC = \ABCProtocol.c
|
||||||
|
|
||||||
|
do {
|
||||||
|
expectTrue(protoErasedSubject.a ===
|
||||||
|
protoErasedSubject[keyPath: protoErasedPathA])
|
||||||
|
|
||||||
|
let newB = LifetimeTracked(4)
|
||||||
|
expectTrue(protoErasedSubject.b ===
|
||||||
|
protoErasedSubject[keyPath: protoErasedPathB])
|
||||||
|
protoErasedSubject[keyPath: protoErasedPathB] = newB
|
||||||
|
expectTrue(protoErasedSubject.b ===
|
||||||
|
protoErasedSubject[keyPath: protoErasedPathB])
|
||||||
|
expectTrue(protoErasedSubject.b === newB)
|
||||||
|
|
||||||
|
let newC = LifetimeTracked(5)
|
||||||
|
expectTrue(protoErasedSubject.c ===
|
||||||
|
protoErasedSubject[keyPath: protoErasedPathC])
|
||||||
|
protoErasedSubject[keyPath: protoErasedPathC] = newC
|
||||||
|
expectTrue(protoErasedSubject.c ===
|
||||||
|
protoErasedSubject[keyPath: protoErasedPathC])
|
||||||
|
expectTrue(protoErasedSubject.c === newC)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
runAllTests()
|
runAllTests()
|
||||||
|
|||||||
Reference in New Issue
Block a user