Skip to content

[cxx-interop] Runtime support for foreign reference types. #59439

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 4 commits into from
Jun 17, 2022
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
43 changes: 43 additions & 0 deletions include/swift/ABI/Metadata.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ template <typename Runtime> struct TargetStructMetadata;
template <typename Runtime> struct TargetOpaqueMetadata;
template <typename Runtime> struct TargetValueMetadata;
template <typename Runtime> struct TargetForeignClassMetadata;
template <typename Runtime> struct TargetForeignReferenceTypeMetadata;
template <typename Runtime> struct TargetContextDescriptor;
template <typename Runtime> class TargetTypeContextDescriptor;
template <typename Runtime> class TargetClassDescriptor;
Expand Down Expand Up @@ -258,6 +259,7 @@ struct TargetMetadata {
case MetadataKind::Class:
case MetadataKind::ObjCClassWrapper:
case MetadataKind::ForeignClass:
case MetadataKind::ForeignReferenceType:
return true;

default:
Expand Down Expand Up @@ -375,6 +377,9 @@ struct TargetMetadata {
case MetadataKind::ForeignClass:
return static_cast<const TargetForeignClassMetadata<Runtime> *>(this)
->Description;
case MetadataKind::ForeignReferenceType:
return static_cast<const TargetForeignReferenceTypeMetadata<Runtime> *>(this)
->Description;
default:
return nullptr;
}
Expand Down Expand Up @@ -1159,6 +1164,44 @@ struct TargetForeignClassMetadata : public TargetForeignTypeMetadata<Runtime> {
};
using ForeignClassMetadata = TargetForeignClassMetadata<InProcess>;

/// The structure of metadata objects for foreign reference types.
/// A foreign reference type is a non-Swift, non-Objective-C foreign type with
/// reference semantics. Foreign reference types are pointers/reference to
/// value types marked with the "import_as_ref" attribute.
///
/// Foreign reference types may have *custom* reference counting operations, or
/// they may be immortal (and therefore trivial).
///
/// We assume for now that foreign reference types are entirely opaque
/// to Swift introspection.
template <typename Runtime>
struct TargetForeignReferenceTypeMetadata : public TargetForeignTypeMetadata<Runtime> {
using StoredPointer = typename Runtime::StoredPointer;

/// An out-of-line description of the type.
TargetSignedPointer<Runtime, const TargetClassDescriptor<Runtime> * __ptrauth_swift_type_descriptor> Description;

/// Reserved space. For now, this should be zero-initialized.
/// If this is used for anything in the future, at least some of these
/// first bits should be flags.
StoredPointer Reserved[1];

ConstTargetMetadataPointer<Runtime, TargetClassDescriptor>
getDescription() const {
return Description;
}

typename Runtime::StoredSignedPointer
getDescriptionAsSignedPointer() const {
return Description;
}

static bool classof(const TargetMetadata<Runtime> *metadata) {
return metadata->getKind() == MetadataKind::ForeignReferenceType;
}
};
using ForeignReferenceTypeMetadata = TargetForeignReferenceTypeMetadata<InProcess>;

/// The common structure of metadata for structs and enums.
template <typename Runtime>
struct TargetValueMetadata : public TargetMetadata<Runtime> {
Expand Down
3 changes: 3 additions & 0 deletions include/swift/ABI/MetadataKind.def
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ NOMINALTYPEMETADATAKIND(Optional, 2 | MetadataKindIsNonHeap)
/// A foreign class, such as a Core Foundation class.
METADATAKIND(ForeignClass, 3 | MetadataKindIsNonHeap)

/// A non-Swift non-Objective-C class type.
METADATAKIND(ForeignReferenceType, 4 | MetadataKindIsNonHeap)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@rjmccall I have no idea what this value should be.


/// A type whose value is not exposed in the metadata system.
METADATAKIND(Opaque, 0 | MetadataKindIsRuntimePrivate | MetadataKindIsNonHeap)

Expand Down
10 changes: 10 additions & 0 deletions include/swift/Remote/MetadataReader.h
Original file line number Diff line number Diff line change
Expand Up @@ -1036,6 +1036,7 @@ class MetadataReader {
TypeCache[MetadataAddress] = BuiltExist;
return BuiltExist;
}
case MetadataKind::ForeignReferenceType:
case MetadataKind::ForeignClass: {
auto descriptorAddr = readAddressOfNominalTypeDescriptor(Meta);
if (!descriptorAddr)
Expand Down Expand Up @@ -1978,6 +1979,8 @@ class MetadataReader {
return _readMetadata<TargetExtendedExistentialTypeMetadata>(address);
case MetadataKind::ForeignClass:
return _readMetadata<TargetForeignClassMetadata>(address);
case MetadataKind::ForeignReferenceType:
return _readMetadata<TargetForeignReferenceTypeMetadata>(address);
case MetadataKind::Function: {
StoredSize flagsValue;
auto flagsAddr =
Expand Down Expand Up @@ -2093,6 +2096,13 @@ class MetadataReader {
return descriptorAddress;
}

case MetadataKind::ForeignReferenceType: {
auto foreignMeta = cast<TargetForeignReferenceTypeMetadata<Runtime>>(metadata);
StoredSignedPointer descriptorAddressSigned = foreignMeta->getDescriptionAsSignedPointer();
StoredPointer descriptorAddress = stripSignedPointer(descriptorAddressSigned);
return descriptorAddress;
}

default:
return 0;
}
Expand Down
22 changes: 22 additions & 0 deletions lib/IRGen/ForeignClassMetadataVisitor.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,28 @@ class ForeignClassMetadataScanner : public ForeignClassMetadataVisitor<Impl> {
}
};

template <class Impl>
class ForeignReferenceTypeMetadataVisitor
: public NominalMetadataVisitor<Impl> {
using super = NominalMetadataVisitor<Impl>;
protected:
ClassDecl *Target;
using super::asImpl;
public:
ForeignReferenceTypeMetadataVisitor(IRGenModule &IGM, ClassDecl *target)
: super(IGM), Target(target) {}

void layout() {
super::layout();
asImpl().addNominalTypeDescriptor();
asImpl().addReservedWord();
}

CanType getTargetType() const {
return Target->getDeclaredType()->getCanonicalType();
}
};

} // end namespace irgen
} // end namespace swift

Expand Down
107 changes: 80 additions & 27 deletions lib/IRGen/GenMeta.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1688,7 +1688,7 @@ namespace {
VTable(IGM.getSILModule().lookUpVTable(getType())),
Resilient(IGM.hasResilientMetadata(Type, ResilienceExpansion::Minimal)) {

if (getType()->isForeign() || Type->isForeignReferenceType())
Copy link
Contributor Author

Choose a reason for hiding this comment

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

(This was just a redundant check.)

if (getType()->isForeign())
return;

MetadataLayout = &IGM.getClassMetadataLayout(Type);
Expand Down Expand Up @@ -1719,7 +1719,6 @@ namespace {
}

void layout() {
assert(!getType()->isForeignReferenceType());
super::layout();
addVTable();
addOverrideTable();
Expand Down Expand Up @@ -2556,15 +2555,6 @@ void irgen::emitLazyTypeContextDescriptor(IRGenModule &IGM,
void irgen::emitLazyTypeMetadata(IRGenModule &IGM, NominalTypeDecl *type) {
eraseExistingTypeContextDescriptor(IGM, type);

// Special case, UFOs are opaque pointers for now.
if (auto cd = dyn_cast<ClassDecl>(type)) {
if (cd->isForeignReferenceType()) {
auto sd = cast<StructDecl>(type->getASTContext().getOpaquePointerDecl());
emitStructMetadata(IGM, sd);
return;
}
}

if (requiresForeignTypeMetadata(type)) {
emitForeignTypeMetadata(IGM, type);
} else if (auto sd = dyn_cast<StructDecl>(type)) {
Expand Down Expand Up @@ -3245,12 +3235,7 @@ static void createNonGenericMetadataAccessFunction(IRGenModule &IGM,
/// Emit the base-offset variable for the class.
static void emitClassMetadataBaseOffset(IRGenModule &IGM,
ClassDecl *classDecl) {
if (classDecl->isForeignReferenceType()) {
classDecl->getASTContext().Diags.diagnose(
classDecl->getLoc(), diag::foreign_reference_types_unsupported.ID,
{});
exit(1);
}
assert(!classDecl->isForeignReferenceType());

// Otherwise, we know the offset at compile time, even if our
// clients do not, so just emit a constant.
Expand Down Expand Up @@ -5444,6 +5429,9 @@ namespace {
ForeignClassMetadataBuilder(IRGenModule &IGM, ClassDecl *target,
ConstantStructBuilder &B)
: ForeignMetadataBuilderBase(IGM, target, B) {
assert(!getTargetType()->isForeignReferenceType() &&
"foreign reference type metadata must be built with the ForeignReferenceTypeMetadataBuilder");

if (IGM.getOptions().LazyInitializeClassMetadata)
CanBeConstant = false;
}
Expand Down Expand Up @@ -5532,6 +5520,61 @@ namespace {
B.addNullPointer(IGM.Int8PtrTy);
}
};

class ForeignReferenceTypeMetadataBuilder;
class ForeignReferenceTypeMetadataBuilderBase :
public ForeignReferenceTypeMetadataVisitor<ForeignReferenceTypeMetadataBuilder> {
protected:
ConstantStructBuilder &B;

ForeignReferenceTypeMetadataBuilderBase(IRGenModule &IGM, ClassDecl *target,
ConstantStructBuilder &B)
: ForeignReferenceTypeMetadataVisitor(IGM, target), B(B) {}
};

/// A builder for ForeignReferenceTypeMetadata.
class ForeignReferenceTypeMetadataBuilder :
public ForeignMetadataBuilderBase<ForeignReferenceTypeMetadataBuilder,
ForeignReferenceTypeMetadataBuilderBase> {
public:
ForeignReferenceTypeMetadataBuilder(IRGenModule &IGM, ClassDecl *target,
ConstantStructBuilder &B)
: ForeignMetadataBuilderBase(IGM, target, B) {
assert(getTargetType()->isForeignReferenceType() &&
"foreign reference type metadata build must be used on foreign reference types.");

if (IGM.getOptions().LazyInitializeClassMetadata)
CanBeConstant = false;
}

void emitInitializeMetadata(IRGenFunction &IGF, llvm::Value *metadata,
MetadataDependencyCollector *collector) {
llvm_unreachable("Not implemented for foreign reference types.");
}

// Visitor methods.

void addValueWitnessTable() {
auto type = getTargetType()->getCanonicalType();
B.add(irgen::emitValueWitnessTable(IGM, type, false, false).getValue());
}

void addMetadataFlags() {
B.addInt(IGM.MetadataKindTy, (unsigned) MetadataKind::ForeignReferenceType);
}

void addNominalTypeDescriptor() {
auto descriptor =
ClassContextDescriptorBuilder(this->IGM, Target, RequireMetadata).emit();
B.addSignedPointer(descriptor,
IGM.getOptions().PointerAuth.TypeDescriptors,
PointerAuthEntity::Special::TypeDescriptor);
}

void addReservedWord() {
B.addNullPointer(IGM.Int8PtrTy);
}
};

/// A builder for ForeignStructMetadata.
class ForeignStructMetadataBuilder :
Expand Down Expand Up @@ -5598,8 +5641,9 @@ bool irgen::requiresForeignTypeMetadata(CanType type) {

bool irgen::requiresForeignTypeMetadata(NominalTypeDecl *decl) {
if (auto *clazz = dyn_cast<ClassDecl>(decl)) {
assert(!clazz->isForeignReferenceType());

if (clazz->isForeignReferenceType())
return true;

switch (clazz->getForeignClassKind()) {
case ClassDecl::ForeignKind::Normal:
case ClassDecl::ForeignKind::RuntimeOnly:
Expand All @@ -5623,16 +5667,25 @@ void irgen::emitForeignTypeMetadata(IRGenModule &IGM, NominalTypeDecl *decl) {
init.setPacked(true);

if (auto classDecl = dyn_cast<ClassDecl>(decl)) {
assert(classDecl->getForeignClassKind() == ClassDecl::ForeignKind::CFType ||
classDecl->isForeignReferenceType());
if (classDecl->isForeignReferenceType()) {
ForeignReferenceTypeMetadataBuilder builder(IGM, classDecl, init);
builder.layout();

ForeignClassMetadataBuilder builder(IGM, classDecl, init);
builder.layout();
IGM.defineTypeMetadata(type, /*isPattern=*/false,
builder.canBeConstant(),
init.finishAndCreateFuture());
builder.createMetadataAccessFunction();
} else {
assert(classDecl->getForeignClassKind() == ClassDecl::ForeignKind::CFType);

IGM.defineTypeMetadata(type, /*isPattern=*/false,
builder.canBeConstant(),
init.finishAndCreateFuture());
builder.createMetadataAccessFunction();
ForeignClassMetadataBuilder builder(IGM, classDecl, init);
builder.layout();

IGM.defineTypeMetadata(type, /*isPattern=*/false,
builder.canBeConstant(),
init.finishAndCreateFuture());
builder.createMetadataAccessFunction();
}
} else if (auto structDecl = dyn_cast<StructDecl>(decl)) {
assert(isa<ClangModuleUnit>(structDecl->getModuleScopeContext()));

Expand Down
9 changes: 0 additions & 9 deletions lib/IRGen/GenProto.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1495,15 +1495,6 @@ llvm::Constant *IRGenModule::getAssociatedTypeWitness(Type type,
GenericSignature sig,
bool inProtocolContext) {
// FIXME: If we can directly reference constant type metadata, do so.

if (type->isForeignReferenceType()) {
type->getASTContext().Diags.diagnose(
type->lookThroughAllOptionalTypes()
->getClassOrBoundGenericClass()
->getLoc(),
diag::foreign_reference_types_unsupported.ID, {});
exit(1);
}

// Form a reference to the mangled name for this type.
assert(!type->hasArchetype() && "type cannot contain archetypes");
Expand Down
2 changes: 0 additions & 2 deletions lib/IRGen/GenReflection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -577,8 +577,6 @@ class ReflectionMetadataBuilder {
CanGenericSignature sig,
MangledTypeRefRole role =
MangledTypeRefRole::Reflection) {
assert(!type->isForeignReferenceType());

B.addRelativeAddress(IGM.getTypeRef(type, sig, role).first);
addBuiltinTypeRefs(type);
}
Expand Down
12 changes: 3 additions & 9 deletions lib/IRGen/MetadataRequest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1090,6 +1090,9 @@ MetadataAccessStrategy irgen::getTypeMetadataAccessStrategy(CanType type) {
assert(type->hasUnboundGenericType());
}

if (type->isForeignReferenceType())
return MetadataAccessStrategy::PublicUniqueAccessor;

if (requiresForeignTypeMetadata(nominal))
return MetadataAccessStrategy::ForeignAccessor;

Expand Down Expand Up @@ -3081,15 +3084,6 @@ llvm::Value *IRGenFunction::emitTypeMetadataRef(CanType type) {
MetadataResponse
IRGenFunction::emitTypeMetadataRef(CanType type,
DynamicMetadataRequest request) {
if (type->isForeignReferenceType()) {
type->getASTContext().Diags.diagnose(
type->lookThroughAllOptionalTypes()
->getClassOrBoundGenericClass()
->getLoc(),
diag::foreign_reference_types_unsupported.ID, {});
exit(1);
}

type = IGM.getRuntimeReifiedType(type);
// Look through any opaque types we're allowed to.
type = IGM.substOpaqueTypesWithUnderlyingTypes(type);
Expand Down
3 changes: 2 additions & 1 deletion stdlib/public/runtime/AnyHashableSupport.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,8 @@ void _swift_makeAnyHashableUpcastingToHashableBaseType(
switch (type->getKind()) {
case MetadataKind::Class:
case MetadataKind::ObjCClassWrapper:
case MetadataKind::ForeignClass: {
case MetadataKind::ForeignClass:
case MetadataKind::ForeignReferenceType: {
#if SWIFT_OBJC_INTEROP
id srcObject;
memcpy(&srcObject, value, sizeof(id));
Expand Down
Loading