Skip to content

[cxx-interop] Implement foreign reference types. #39605

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
Dec 8, 2021
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
30 changes: 25 additions & 5 deletions include/swift/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include "swift/AST/GenericParamKey.h"
#include "swift/AST/IfConfigClause.h"
#include "swift/AST/LayoutConstraint.h"
#include "swift/AST/ReferenceCounting.h"
#include "swift/AST/StorageImpl.h"
#include "swift/AST/TypeAlignments.h"
#include "swift/AST/TypeWalker.h"
Expand All @@ -37,10 +38,10 @@
#include "swift/Basic/Compiler.h"
#include "swift/Basic/Debug.h"
#include "swift/Basic/InlineBitfield.h"
#include "swift/Basic/Located.h"
#include "swift/Basic/NullablePtr.h"
#include "swift/Basic/OptionalEnum.h"
#include "swift/Basic/Range.h"
#include "swift/Basic/Located.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/Support/TrailingObjects.h"
#include <type_traits>
Expand Down Expand Up @@ -3906,7 +3907,8 @@ class ClassDecl final : public NominalTypeDecl {
///
/// \see getForeignClassKind
bool isForeign() const {
return getForeignClassKind() != ForeignKind::Normal;
return getForeignClassKind() != ForeignKind::Normal ||
const_cast<ClassDecl *>(this)->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 is not ideal, but I think it's OK because we never construct a const decl, right?

}

/// Whether the class is (known to be) a default actor.
Expand Down Expand Up @@ -3941,9 +3943,22 @@ class ClassDecl final : public NominalTypeDecl {
bool isNativeNSObjectSubclass() const;

/// Whether the class uses the ObjC object model (reference counting,
/// allocation, etc.) instead of the Swift model.
bool usesObjCObjectModel() const {
return checkAncestry(AncestryFlags::ObjCObjectModel);
/// allocation, etc.), the Swift model, or has no reference counting at all.
ReferenceCounting getObjectModel() {
if (isForeignReferenceType())
return ReferenceCounting::None;

if (checkAncestry(AncestryFlags::ObjCObjectModel))
return ReferenceCounting::ObjC;

return ReferenceCounting::Native;
}

LayoutConstraintKind getLayoutConstraintKind() {
if (getObjectModel() == ReferenceCounting::ObjC)
return LayoutConstraintKind::Class;

return LayoutConstraintKind::NativeClass;
}

/// Returns true if the class has designated initializers that are not listed
Expand Down Expand Up @@ -4065,6 +4080,11 @@ class ClassDecl final : public NominalTypeDecl {
bool hasKnownSwiftImplementation() const {
return !hasClangNode();
}

/// Used to determine if this class decl is a foriegn reference type. I.e., a
/// non-reference-counted swift reference type that was imported from a C++
/// record.
bool isForeignReferenceType();
};

/// A convenience wrapper around the \c SelfReferencePosition::Kind enum.
Expand Down
5 changes: 5 additions & 0 deletions include/swift/AST/DiagnosticsIRGen.def
Original file line number Diff line number Diff line change
Expand Up @@ -59,5 +59,10 @@ ERROR(temporary_allocation_alignment_not_positive,none,
ERROR(temporary_allocation_alignment_not_power_of_2,none,
"alignment value must be a power of two", ())

ERROR(foreign_reference_types_unsupported,none,
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 and @beccadax I need to add a test for this new error, but other than that, this should be ready for re-review.

"attempt to use a foreign reference type in a generic context. "
"Foreign reference types are currently not supported. Using foreign "
"reference types in a generic context is not yet implemented.", ())

#define UNDEFINE_DIAGNOSTIC_MACROS
#include "DefineDiagnosticMacros.h"
4 changes: 4 additions & 0 deletions include/swift/AST/ReferenceCounting.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ enum class ReferenceCounting : uint8_t {
/// Blocks are always ObjC reference counting compatible.
ObjC,

/// The object has no reference counting. This is used by foreign reference
/// types.
None,

/// The object uses _Block_copy/_Block_release reference counting.
///
/// This is a strict subset of ObjC; all blocks are also ObjC reference
Expand Down
2 changes: 2 additions & 0 deletions include/swift/AST/Type.h
Original file line number Diff line number Diff line change
Expand Up @@ -488,6 +488,8 @@ class CanType : public Type {
NominalTypeDecl *getAnyNominal() const;
GenericTypeDecl *getAnyGeneric() const;

bool isForeignReferenceType(); // in Types.h

CanType getOptionalObjectType() const {
return getOptionalObjectTypeImpl(*this);
}
Expand Down
13 changes: 7 additions & 6 deletions include/swift/AST/Types.h
Original file line number Diff line number Diff line change
Expand Up @@ -811,16 +811,19 @@ class alignas(1 << TypeAlignInBits) TypeBase

/// If this is a class type or a bound generic class type, returns the
/// (possibly generic) class.
ClassDecl *getClassOrBoundGenericClass();
ClassDecl *getClassOrBoundGenericClass() const;

/// If this is a struct type or a bound generic struct type, returns
/// the (possibly generic) class.
StructDecl *getStructOrBoundGenericStruct();

/// If this is an enum or a bound generic enum type, returns the
/// (possibly generic) enum.
EnumDecl *getEnumOrBoundGenericEnum();


/// If this is a class, check if this class is a foreign reference type.
bool isForeignReferenceType();

/// Determine whether this type may have a superclass, which holds for
/// classes, bound generic classes, and archetypes that are only instantiable
/// with a class type.
Expand Down Expand Up @@ -6198,7 +6201,7 @@ inline bool TypeBase::canDynamicallyBeOptionalType(bool includeExistential) {
return isArchetypeOrExistential && !T.isAnyClassReferenceType();
}

inline ClassDecl *TypeBase::getClassOrBoundGenericClass() {
inline ClassDecl *TypeBase::getClassOrBoundGenericClass() const {
return getCanonicalType().getClassOrBoundGenericClass();
}

Expand Down Expand Up @@ -6262,8 +6265,6 @@ inline GenericTypeDecl *TypeBase::getAnyGeneric() {
return getCanonicalType().getAnyGeneric();
}



inline bool TypeBase::isBuiltinIntegerType(unsigned n) {
if (auto intTy = dyn_cast<BuiltinIntegerType>(getCanonicalType()))
return intTy->getWidth().isFixedWidth()
Expand Down
4 changes: 2 additions & 2 deletions include/swift/ClangImporter/ClangImporterRequests.h
Original file line number Diff line number Diff line change
Expand Up @@ -132,10 +132,10 @@ class CXXNamespaceMemberLookup

/// The input type for a record member lookup request.
struct ClangRecordMemberLookupDescriptor final {
StructDecl *recordDecl;
NominalTypeDecl *recordDecl;
DeclName name;

ClangRecordMemberLookupDescriptor(StructDecl *recordDecl, DeclName name)
ClangRecordMemberLookupDescriptor(NominalTypeDecl *recordDecl, DeclName name)
: recordDecl(recordDecl), name(name) {
assert(isa<clang::RecordDecl>(recordDecl->getClangDecl()));
}
Expand Down
8 changes: 7 additions & 1 deletion include/swift/SIL/SILType.h
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,13 @@ class SILType {
NominalTypeDecl *getNominalOrBoundGenericNominal() const {
return getASTType().getNominalOrBoundGenericNominal();
}


/// If this type maps to a Swift class, check if that class is a foreign
/// reference type.
bool isForeignReferenceType() const {
return getASTType().isForeignReferenceType();
}

/// True if the type is an address type.
bool isAddress() const { return getCategory() == SILValueCategory::Address; }

Expand Down
3 changes: 2 additions & 1 deletion lib/AST/ClangTypeConverter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -545,7 +545,8 @@ ClangTypeConverter::visitBoundGenericType(BoundGenericType *type) {
auto args = type->getGenericArgs();
assert((args.size() == 1) && "Optional should have 1 generic argument.");
clang::QualType innerTy = convert(args[0]);
if (swift::canImportAsOptional(innerTy.getTypePtrOrNull()))
if (swift::canImportAsOptional(innerTy.getTypePtrOrNull()) ||
args[0]->isForeignReferenceType())
return innerTy;
return clang::QualType();
}
Expand Down
10 changes: 9 additions & 1 deletion lib/AST/Decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1957,7 +1957,11 @@ static bool isPolymorphic(const AbstractStorageDecl *storage) {
return true;

if (auto *classDecl = dyn_cast<ClassDecl>(storage->getDeclContext())) {
if (storage->isFinal() || classDecl->isFinal())
// Accesses to members of foreign reference types should be made directly
// to storage as these are references to clang records which are not allowed
// to have dynamic dispatch.
if (storage->isFinal() || classDecl->isFinal() ||
classDecl->isForeignReferenceType())
Copy link
Contributor

Choose a reason for hiding this comment

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

Why is this here? Imported classes can have polymorphic operations. At the very least, this needs a comment.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added a comment.

return false;

return true;
Expand Down Expand Up @@ -4737,6 +4741,10 @@ bool ClassDecl::walkSuperclasses(
return false;
}

bool ClassDecl::isForeignReferenceType() {
return getClangDecl() && isa<clang::RecordDecl>(getClangDecl());
}

EnumCaseDecl *EnumCaseDecl::create(SourceLoc CaseLoc,
ArrayRef<EnumElementDecl *> Elements,
DeclContext *DC) {
Expand Down
7 changes: 2 additions & 5 deletions lib/AST/GenericSignatureBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4443,11 +4443,8 @@ bool GenericSignatureBuilder::updateSuperclass(
auto layoutReqSource =
source.getSource(*this, type)->viaLayout(*this, superclass);

auto layout =
LayoutConstraint::getLayoutConstraint(
superclass->getClassOrBoundGenericClass()->usesObjCObjectModel()
? LayoutConstraintKind::Class
: LayoutConstraintKind::NativeClass,
auto layout = LayoutConstraint::getLayoutConstraint(
superclass->getClassOrBoundGenericClass()->getLayoutConstraintKind(),
getASTContext());
addLayoutRequirementDirect(type, layout, layoutReqSource);
return true;
Expand Down
2 changes: 1 addition & 1 deletion lib/AST/NameLookup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1478,7 +1478,7 @@ DirectLookupRequest::evaluate(Evaluator &evaluator,
} else if (isa_and_nonnull<clang::RecordDecl>(decl->getClangDecl())) {
auto allFound = evaluateOrDefault(
ctx.evaluator,
ClangRecordMemberLookup({cast<StructDecl>(decl), name}), {});
ClangRecordMemberLookup({cast<NominalTypeDecl>(decl), name}), {});
// Add all the members we found, later we'll combine these with the
// existing members.
for (auto found : allFound)
Expand Down
4 changes: 1 addition & 3 deletions lib/AST/RequirementMachine/RequirementLowering.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -924,9 +924,7 @@ void RuleBuilder::addRequirement(const Requirement &req,
// Build the symbol [layout: L].
auto layout =
LayoutConstraint::getLayoutConstraint(
otherType->getClassOrBoundGenericClass()->usesObjCObjectModel()
? LayoutConstraintKind::Class
: LayoutConstraintKind::NativeClass,
otherType->getClassOrBoundGenericClass()->getLayoutConstraintKind(),
Context.getASTContext());
auto layoutSymbol = Symbol::forLayout(layout, Context);

Expand Down
32 changes: 20 additions & 12 deletions lib/AST/Type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -846,7 +846,7 @@ Type TypeBase::stripConcurrency(bool recurse, bool dropGlobalActor) {
bool TypeBase::isAnyObject() {
auto canTy = getCanonicalType();

if (!canTy.isExistentialType())
if (!canTy.isExistentialType() || canTy.isForeignReferenceType())
return false;

return canTy.getExistentialLayout().isAnyObject();
Expand Down Expand Up @@ -5362,19 +5362,16 @@ bool UnownedStorageType::isLoadable(ResilienceExpansion resilience) const {
return ty->getReferenceCounting() == ReferenceCounting::Native;
}

static ReferenceCounting getClassReferenceCounting(ClassDecl *theClass) {
return (theClass->usesObjCObjectModel()
? ReferenceCounting::ObjC
: ReferenceCounting::Native);
}

ReferenceCounting TypeBase::getReferenceCounting() {
CanType type = getCanonicalType();
ASTContext &ctx = type->getASTContext();

// In the absence of Objective-C interoperability, everything uses native
// reference counting or is the builtin BridgeObject.
if (!ctx.LangOpts.EnableObjCInterop) {
if (isForeignReferenceType())
return ReferenceCounting::None;

return type->getKind() == TypeKind::BuiltinBridgeObject
? ReferenceCounting::Bridge
: ReferenceCounting::Native;
Expand All @@ -5394,13 +5391,12 @@ ReferenceCounting TypeBase::getReferenceCounting() {
return ReferenceCounting::Bridge;

case TypeKind::Class:
return getClassReferenceCounting(cast<ClassType>(type)->getDecl());
return cast<ClassType>(type)->getDecl()->getObjectModel();
case TypeKind::BoundGenericClass:
return getClassReferenceCounting(
cast<BoundGenericClassType>(type)->getDecl());
return cast<BoundGenericClassType>(type)->getDecl()->getObjectModel();
case TypeKind::UnboundGeneric:
return getClassReferenceCounting(
cast<ClassDecl>(cast<UnboundGenericType>(type)->getDecl()));
return cast<ClassDecl>(cast<UnboundGenericType>(type)->getDecl())
->getObjectModel();

case TypeKind::DynamicSelf:
return cast<DynamicSelfType>(type).getSelfType()
Expand Down Expand Up @@ -5612,6 +5608,18 @@ TypeBase::getAutoDiffTangentSpace(LookupConformanceFn lookupConformance) {
return cache(None);
}

bool TypeBase::isForeignReferenceType() {
if (auto *classDecl = lookThroughAllOptionalTypes()->getClassOrBoundGenericClass())
return classDecl->isForeignReferenceType();
return false;
}

bool CanType::isForeignReferenceType() {
if (auto *classDecl = getPointer()->lookThroughAllOptionalTypes()->getClassOrBoundGenericClass())
Copy link
Contributor

Choose a reason for hiding this comment

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

Looks like you duplicate the logic here. Can you call getPointer()->isForeignReferenceType() instead?

return classDecl->isForeignReferenceType();
return false;
}

// Creates an `AnyFunctionType` from the given parameters, result type,
// generic signature, and `ExtInfo`.
static AnyFunctionType *
Expand Down
2 changes: 1 addition & 1 deletion lib/ClangImporter/ClangImporter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4141,7 +4141,7 @@ TinyPtrVector<ValueDecl *> CXXNamespaceMemberLookup::evaluate(

TinyPtrVector<ValueDecl *> ClangRecordMemberLookup::evaluate(
Evaluator &evaluator, ClangRecordMemberLookupDescriptor desc) const {
StructDecl *recordDecl = desc.recordDecl;
NominalTypeDecl *recordDecl = desc.recordDecl;
DeclName name = desc.name;

auto &ctx = recordDecl->getASTContext();
Expand Down
Loading