Skip to content

Make a temporary existential when reflecting weak properties #4397

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
Aug 20, 2016
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
12 changes: 9 additions & 3 deletions include/swift/Runtime/Metadata.h
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ using TargetFarRelativeIndirectablePointer
= typename Runtime::template FarRelativeIndirectablePointer<Pointee,Nullable>;

struct HeapObject;
struct WeakReference;

template <typename Runtime> struct TargetMetadata;
using Metadata = TargetMetadata<InProcess>;
Expand Down Expand Up @@ -2327,8 +2328,9 @@ using OpaqueExistentialContainer
= TargetOpaqueExistentialContainer<InProcess>;

/// The basic layout of a class-bounded existential type.
struct ClassExistentialContainer {
void *Value;
template <typename ContainedValue>
struct ClassExistentialContainerImpl {
ContainedValue Value;

const WitnessTable **getWitnessTables() {
return reinterpret_cast<const WitnessTable**>(this + 1);
Expand All @@ -2337,11 +2339,15 @@ struct ClassExistentialContainer {
return reinterpret_cast<const WitnessTable* const *>(this + 1);
}

void copyTypeInto(ClassExistentialContainer *dest, unsigned numTables) const {
void copyTypeInto(ClassExistentialContainerImpl *dest,
unsigned numTables) const {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor request (totally unnecessary for 3.0): you could make this templated over the dest's value type, since it doesn't touch that field at all.

for (unsigned i = 0; i != numTables; ++i)
dest->getWitnessTables()[i] = getWitnessTables()[i];
}
};
using ClassExistentialContainer = ClassExistentialContainerImpl<void *>;
using WeakClassExistentialContainer =
ClassExistentialContainerImpl<WeakReference>;

/// The possible physical representations of existential types.
enum class ExistentialTypeRepresentation {
Expand Down
50 changes: 39 additions & 11 deletions stdlib/public/runtime/Reflection.mm
Original file line number Diff line number Diff line change
Expand Up @@ -403,27 +403,55 @@ static bool loadSpecialReferenceStorage(HeapObject *owner,
OpaqueValue *fieldData,
const FieldType fieldType,
Mirror *outMirror) {
// TODO: Switch over the other kinds of reference storage here:
// - unowned(safe)
// - unowned(unsafe)
// - class-bound existentials
// - Optional existential types
// This will require a change in the two low flag bits in the field type
// returned from the field type accessor generated by IRGen.

// isWeak() implies a reference type via Sema.
if (!fieldType.isWeak())
return false;

auto type = fieldType.getType();
assert(type->getKind() == MetadataKind::Optional);

auto weakField = reinterpret_cast<WeakReference *>(fieldData);
auto strongValue = swift_unknownWeakLoadStrong(weakField);
fieldData = reinterpret_cast<OpaqueValue *>(&strongValue);

// Now that we have a strong reference, we need to create a temporary buffer
// from which to copy the whole value, which might be a native class-bound
// existential, which means we also need to copy n witness tables, for
// however many protocols are in the protocol composition. For example, if we
// are copying a:
// weak var myWeakProperty : (Protocol1 & Protocol2)?
// then we need to copy three values:
// - the instance
// - the witness table for Protocol1
// - the witness table for Protocol2

auto weakContainer =
reinterpret_cast<WeakClassExistentialContainer *>(fieldData);

// Create a temporary existential where we can put the strong reference.
// The allocateBuffer value witness requires a ValueBuffer to own the
// allocated storage.
ValueBuffer temporaryBuffer;

auto temporaryValue =
reinterpret_cast<ClassExistentialContainer *>(
type->vw_allocateBuffer(&temporaryBuffer));

// Now copy the entire value out of the parent, which will include the
// witness tables.
temporaryValue->Value = strongValue;
auto valueWitnessesSize = type->getValueWitnesses()->getSize() -
sizeof(WeakClassExistentialContainer);
memcpy(temporaryValue->getWitnessTables(), weakContainer->getWitnessTables(),
valueWitnessesSize);

// This MagicMirror constructor creates a box to hold the loaded refernce
// value, which becomes the new owner for the value.
new (outMirror) MagicMirror(fieldData, fieldType.getType(), /*take*/ true);
new (outMirror) MagicMirror(reinterpret_cast<OpaqueValue *>(temporaryValue),
type, /*take*/ true);

type->vw_deallocateBuffer(&temporaryBuffer);

// However, swift_StructMirror_subscript and swift_ClassMirror_subscript
// swift_StructMirror_subscript and swift_ClassMirror_subscript
// requires that the owner be consumed. Since we have the new heap box as the
// owner now, we need to release the old owner to maintain the contract.
if (owner->metadata->isAnyClass())
Expand Down
Loading