From fc3b3a1d71fbc984a346a42ab62a6e05fffe9c36 Mon Sep 17 00:00:00 2001 From: zoecarver Date: Wed, 29 Sep 2021 15:29:07 -0700 Subject: [PATCH] [cxx-interop] Implement foreign reference types. This is an expiremental feature to allow an attribute, `import_as_ref`, to import a C++ record as a non-reference-counted reference type in Swift. --- include/swift/AST/Decl.h | 30 +++- include/swift/AST/DiagnosticsIRGen.def | 5 + include/swift/AST/ReferenceCounting.h | 4 + include/swift/AST/Type.h | 2 + include/swift/AST/Types.h | 13 +- .../ClangImporter/ClangImporterRequests.h | 4 +- include/swift/SIL/SILType.h | 8 +- lib/AST/ClangTypeConverter.cpp | 3 +- lib/AST/Decl.cpp | 10 +- lib/AST/GenericSignatureBuilder.cpp | 7 +- lib/AST/NameLookup.cpp | 2 +- .../RequirementLowering.cpp | 4 +- lib/AST/Type.cpp | 32 ++-- lib/ClangImporter/ClangImporter.cpp | 2 +- lib/ClangImporter/ImportDecl.cpp | 104 +++++++++--- lib/ClangImporter/ImportType.cpp | 17 +- lib/ClangImporter/ImporterImpl.h | 2 +- lib/IRGen/GenCall.cpp | 16 +- lib/IRGen/GenClass.cpp | 4 +- lib/IRGen/GenDecl.cpp | 2 +- lib/IRGen/GenHeap.cpp | 125 +++++++------- lib/IRGen/GenMeta.cpp | 22 ++- lib/IRGen/GenProto.cpp | 10 ++ lib/IRGen/GenReflection.cpp | 12 ++ lib/IRGen/GenType.cpp | 1 + lib/IRGen/GenValueWitness.cpp | 1 + lib/IRGen/IRGenMangler.cpp | 3 +- lib/IRGen/IRGenModule.h | 1 + lib/IRGen/IRGenSIL.cpp | 5 +- lib/IRGen/MetadataLayout.cpp | 9 +- lib/IRGen/MetadataRequest.cpp | 13 ++ lib/PrintAsObjC/DeclAndTypePrinter.cpp | 3 + lib/SIL/IR/SILFunctionType.cpp | 13 +- lib/SIL/IR/TypeLowering.cpp | 6 + lib/SIL/Utils/InstructionUtils.cpp | 9 +- lib/SIL/Verifier/SILVerifier.cpp | 14 +- lib/SILGen/SILGenConstructor.cpp | 2 +- lib/Sema/CSSimplify.cpp | 4 + lib/Sema/TypeCheckStorage.cpp | 9 +- .../foreign-reference/Inputs/module.modulemap | 24 +++ .../Cxx/foreign-reference/Inputs/move-only.h | 76 +++++++++ .../Cxx/foreign-reference/Inputs/nullable.h | 30 ++++ .../Cxx/foreign-reference/Inputs/pod.h | 122 ++++++++++++++ .../Cxx/foreign-reference/Inputs/singleton.h | 90 ++++++++++ .../Cxx/foreign-reference/Inputs/visibility.h | 25 +++ .../foreign-reference/Inputs/witness-table.h | 53 ++++++ .../error-as-class-member.swift | 27 +++ .../error-as-struct-member.swift | 25 +++ .../error-in-generic-context.swift | 27 +++ .../foreign-reference/move-only-irgen.swift | 29 ++++ .../move-only-module-interface.swift | 32 ++++ .../foreign-reference/move-only-silgen.swift | 33 ++++ .../Cxx/foreign-reference/move-only.swift | 43 +++++ .../foreign-reference/not-any-object.swift | 29 ++++ .../nullable-module-interface.swift | 15 ++ .../Cxx/foreign-reference/nullable.swift | 34 ++++ .../Cxx/foreign-reference/pod-irgen.swift | 33 ++++ .../pod-module-interface.swift | 77 +++++++++ .../Cxx/foreign-reference/pod-silgen.swift | 39 +++++ test/Interop/Cxx/foreign-reference/pod.swift | 154 ++++++++++++++++++ .../foreign-reference/singleton-irgen.swift | 32 ++++ .../singleton-module-interface.swift | 37 +++++ .../foreign-reference/singleton-silgen.swift | 41 +++++ .../Cxx/foreign-reference/singleton.swift | 74 +++++++++ .../Cxx/foreign-reference/witness-table.swift | 57 +++++++ 65 files changed, 1645 insertions(+), 146 deletions(-) create mode 100644 test/Interop/Cxx/foreign-reference/Inputs/module.modulemap create mode 100644 test/Interop/Cxx/foreign-reference/Inputs/move-only.h create mode 100644 test/Interop/Cxx/foreign-reference/Inputs/nullable.h create mode 100644 test/Interop/Cxx/foreign-reference/Inputs/pod.h create mode 100644 test/Interop/Cxx/foreign-reference/Inputs/singleton.h create mode 100644 test/Interop/Cxx/foreign-reference/Inputs/visibility.h create mode 100644 test/Interop/Cxx/foreign-reference/Inputs/witness-table.h create mode 100644 test/Interop/Cxx/foreign-reference/error-as-class-member.swift create mode 100644 test/Interop/Cxx/foreign-reference/error-as-struct-member.swift create mode 100644 test/Interop/Cxx/foreign-reference/error-in-generic-context.swift create mode 100644 test/Interop/Cxx/foreign-reference/move-only-irgen.swift create mode 100644 test/Interop/Cxx/foreign-reference/move-only-module-interface.swift create mode 100644 test/Interop/Cxx/foreign-reference/move-only-silgen.swift create mode 100644 test/Interop/Cxx/foreign-reference/move-only.swift create mode 100644 test/Interop/Cxx/foreign-reference/not-any-object.swift create mode 100644 test/Interop/Cxx/foreign-reference/nullable-module-interface.swift create mode 100644 test/Interop/Cxx/foreign-reference/nullable.swift create mode 100644 test/Interop/Cxx/foreign-reference/pod-irgen.swift create mode 100644 test/Interop/Cxx/foreign-reference/pod-module-interface.swift create mode 100644 test/Interop/Cxx/foreign-reference/pod-silgen.swift create mode 100644 test/Interop/Cxx/foreign-reference/pod.swift create mode 100644 test/Interop/Cxx/foreign-reference/singleton-irgen.swift create mode 100644 test/Interop/Cxx/foreign-reference/singleton-module-interface.swift create mode 100644 test/Interop/Cxx/foreign-reference/singleton-silgen.swift create mode 100644 test/Interop/Cxx/foreign-reference/singleton.swift create mode 100644 test/Interop/Cxx/foreign-reference/witness-table.swift diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index b313d264736df..4f121656993a0 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -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" @@ -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 @@ -3906,7 +3907,8 @@ class ClassDecl final : public NominalTypeDecl { /// /// \see getForeignClassKind bool isForeign() const { - return getForeignClassKind() != ForeignKind::Normal; + return getForeignClassKind() != ForeignKind::Normal || + const_cast(this)->isForeignReferenceType(); } /// Whether the class is (known to be) a default actor. @@ -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 @@ -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. diff --git a/include/swift/AST/DiagnosticsIRGen.def b/include/swift/AST/DiagnosticsIRGen.def index f4f9b068528d8..db23d69928b7a 100644 --- a/include/swift/AST/DiagnosticsIRGen.def +++ b/include/swift/AST/DiagnosticsIRGen.def @@ -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, + "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" diff --git a/include/swift/AST/ReferenceCounting.h b/include/swift/AST/ReferenceCounting.h index 3eaf4a1acf788..26bf440ef3284 100644 --- a/include/swift/AST/ReferenceCounting.h +++ b/include/swift/AST/ReferenceCounting.h @@ -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 diff --git a/include/swift/AST/Type.h b/include/swift/AST/Type.h index 687070e5a4e8c..467753c4df00d 100644 --- a/include/swift/AST/Type.h +++ b/include/swift/AST/Type.h @@ -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); } diff --git a/include/swift/AST/Types.h b/include/swift/AST/Types.h index b8f337344569c..9ddeb6462c201 100644 --- a/include/swift/AST/Types.h +++ b/include/swift/AST/Types.h @@ -811,8 +811,8 @@ 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(); @@ -820,7 +820,10 @@ class alignas(1 << TypeAlignInBits) TypeBase /// 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. @@ -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(); } @@ -6262,8 +6265,6 @@ inline GenericTypeDecl *TypeBase::getAnyGeneric() { return getCanonicalType().getAnyGeneric(); } - - inline bool TypeBase::isBuiltinIntegerType(unsigned n) { if (auto intTy = dyn_cast(getCanonicalType())) return intTy->getWidth().isFixedWidth() diff --git a/include/swift/ClangImporter/ClangImporterRequests.h b/include/swift/ClangImporter/ClangImporterRequests.h index d6fea3d64e88c..4376b23019913 100644 --- a/include/swift/ClangImporter/ClangImporterRequests.h +++ b/include/swift/ClangImporter/ClangImporterRequests.h @@ -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(recordDecl->getClangDecl())); } diff --git a/include/swift/SIL/SILType.h b/include/swift/SIL/SILType.h index 0747aa231622d..97a6e9b760972 100644 --- a/include/swift/SIL/SILType.h +++ b/include/swift/SIL/SILType.h @@ -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; } diff --git a/lib/AST/ClangTypeConverter.cpp b/lib/AST/ClangTypeConverter.cpp index 76ddd8a4a014d..6e533e1ab89e5 100644 --- a/lib/AST/ClangTypeConverter.cpp +++ b/lib/AST/ClangTypeConverter.cpp @@ -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(); } diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 0279f72789134..cdd6789225118 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -1957,7 +1957,11 @@ static bool isPolymorphic(const AbstractStorageDecl *storage) { return true; if (auto *classDecl = dyn_cast(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()) return false; return true; @@ -4737,6 +4741,10 @@ bool ClassDecl::walkSuperclasses( return false; } +bool ClassDecl::isForeignReferenceType() { + return getClangDecl() && isa(getClangDecl()); +} + EnumCaseDecl *EnumCaseDecl::create(SourceLoc CaseLoc, ArrayRef Elements, DeclContext *DC) { diff --git a/lib/AST/GenericSignatureBuilder.cpp b/lib/AST/GenericSignatureBuilder.cpp index c37d03593d908..bb8f0867ef414 100644 --- a/lib/AST/GenericSignatureBuilder.cpp +++ b/lib/AST/GenericSignatureBuilder.cpp @@ -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; diff --git a/lib/AST/NameLookup.cpp b/lib/AST/NameLookup.cpp index 0c5f2fd44ba7b..dfb758cc45ca7 100644 --- a/lib/AST/NameLookup.cpp +++ b/lib/AST/NameLookup.cpp @@ -1478,7 +1478,7 @@ DirectLookupRequest::evaluate(Evaluator &evaluator, } else if (isa_and_nonnull(decl->getClangDecl())) { auto allFound = evaluateOrDefault( ctx.evaluator, - ClangRecordMemberLookup({cast(decl), name}), {}); + ClangRecordMemberLookup({cast(decl), name}), {}); // Add all the members we found, later we'll combine these with the // existing members. for (auto found : allFound) diff --git a/lib/AST/RequirementMachine/RequirementLowering.cpp b/lib/AST/RequirementMachine/RequirementLowering.cpp index d0edad46f0f0e..20a45eab7330b 100644 --- a/lib/AST/RequirementMachine/RequirementLowering.cpp +++ b/lib/AST/RequirementMachine/RequirementLowering.cpp @@ -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); diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp index e27df1cc49c70..d9dc6466d3b42 100644 --- a/lib/AST/Type.cpp +++ b/lib/AST/Type.cpp @@ -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(); @@ -5362,12 +5362,6 @@ 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(); @@ -5375,6 +5369,9 @@ ReferenceCounting TypeBase::getReferenceCounting() { // 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; @@ -5394,13 +5391,12 @@ ReferenceCounting TypeBase::getReferenceCounting() { return ReferenceCounting::Bridge; case TypeKind::Class: - return getClassReferenceCounting(cast(type)->getDecl()); + return cast(type)->getDecl()->getObjectModel(); case TypeKind::BoundGenericClass: - return getClassReferenceCounting( - cast(type)->getDecl()); + return cast(type)->getDecl()->getObjectModel(); case TypeKind::UnboundGeneric: - return getClassReferenceCounting( - cast(cast(type)->getDecl())); + return cast(cast(type)->getDecl()) + ->getObjectModel(); case TypeKind::DynamicSelf: return cast(type).getSelfType() @@ -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()) + return classDecl->isForeignReferenceType(); + return false; +} + // Creates an `AnyFunctionType` from the given parameters, result type, // generic signature, and `ExtInfo`. static AnyFunctionType * diff --git a/lib/ClangImporter/ClangImporter.cpp b/lib/ClangImporter/ClangImporter.cpp index 1f97f4f485d9e..f9e6c46538864 100644 --- a/lib/ClangImporter/ClangImporter.cpp +++ b/lib/ClangImporter/ClangImporter.cpp @@ -4141,7 +4141,7 @@ TinyPtrVector CXXNamespaceMemberLookup::evaluate( TinyPtrVector ClangRecordMemberLookup::evaluate( Evaluator &evaluator, ClangRecordMemberLookupDescriptor desc) const { - StructDecl *recordDecl = desc.recordDecl; + NominalTypeDecl *recordDecl = desc.recordDecl; DeclName name = desc.name; auto &ctx = recordDecl->getASTContext(); diff --git a/lib/ClangImporter/ImportDecl.cpp b/lib/ClangImporter/ImportDecl.cpp index a6204786143ba..7d0ae97255c2c 100644 --- a/lib/ClangImporter/ImportDecl.cpp +++ b/lib/ClangImporter/ImportDecl.cpp @@ -709,7 +709,7 @@ static AccessorDecl *makeStructRawValueGetter( } static AccessorDecl *makeFieldGetterDecl(ClangImporter::Implementation &Impl, - StructDecl *importedDecl, + NominalTypeDecl *importedDecl, VarDecl *importedFieldDecl, ClangNode clangNode = ClangNode()) { auto &C = Impl.SwiftContext; @@ -737,7 +737,7 @@ static AccessorDecl *makeFieldGetterDecl(ClangImporter::Implementation &Impl, } static AccessorDecl *makeFieldSetterDecl(ClangImporter::Implementation &Impl, - StructDecl *importedDecl, + NominalTypeDecl *importedDecl, VarDecl *importedFieldDecl, ClangNode clangNode = ClangNode()) { auto &C = Impl.SwiftContext; @@ -879,7 +879,7 @@ static std::pair makeIndirectFieldAccessors(ClangImporter::Implementation &Impl, const clang::IndirectFieldDecl *indirectField, ArrayRef members, - StructDecl *importedStructDecl, + NominalTypeDecl *importedStructDecl, VarDecl *importedFieldDecl) { auto &C = Impl.SwiftContext; @@ -1046,7 +1046,7 @@ synthesizeUnionFieldSetterBody(AbstractFunctionDecl *afd, void *context) { /// \returns a pair of the getter and setter function decls. static std::pair makeUnionFieldAccessors(ClangImporter::Implementation &Impl, - StructDecl *importedUnionDecl, + NominalTypeDecl *importedUnionDecl, VarDecl *importedFieldDecl) { auto &C = Impl.SwiftContext; @@ -1070,7 +1070,7 @@ makeUnionFieldAccessors(ClangImporter::Implementation &Impl, static clang::DeclarationName getAccessorDeclarationName(clang::ASTContext &Ctx, - StructDecl *structDecl, + NominalTypeDecl *structDecl, VarDecl *fieldDecl, const char *suffix) { std::string id; @@ -1098,7 +1098,7 @@ getAccessorDeclarationName(clang::ASTContext &Ctx, static std::pair makeBitFieldAccessors(ClangImporter::Implementation &Impl, clang::RecordDecl *structDecl, - StructDecl *importedStructDecl, + NominalTypeDecl *importedStructDecl, clang::FieldDecl *fieldDecl, VarDecl *importedFieldDecl) { clang::ASTContext &Ctx = Impl.getClangASTContext(); @@ -1305,7 +1305,7 @@ synthesizeStructDefaultConstructorBody(AbstractFunctionDecl *afd, /// Create a default constructor that initializes a struct to zero. static ConstructorDecl * createDefaultConstructor(ClangImporter::Implementation &Impl, - StructDecl *structDecl) { + NominalTypeDecl *structDecl) { auto &context = Impl.SwiftContext; auto emptyPL = ParameterList::createEmpty(context); @@ -1400,7 +1400,7 @@ synthesizeValueConstructorBody(AbstractFunctionDecl *afd, void *context) { /// Create a constructor that initializes a struct from its members. static ConstructorDecl * createValueConstructor(ClangImporter::Implementation &Impl, - StructDecl *structDecl, ArrayRef members, + NominalTypeDecl *structDecl, ArrayRef members, bool wantCtorParamNames, bool wantBody) { auto &context = Impl.SwiftContext; @@ -3282,7 +3282,18 @@ namespace { return result; } + static bool hasImportAsRefAttr(const clang::RecordDecl *decl) { + return decl->hasAttrs() && llvm::any_of(decl->getAttrs(), [](auto *attr) { + if (auto swiftAttr = dyn_cast(attr)) + return swiftAttr->getAttribute() == "import_as_ref"; + return false; + }); + } + bool isCxxRecordImportable(const clang::CXXRecordDecl *decl) { + if (hasImportAsRefAttr(decl)) + return true; + if (auto dtor = decl->getDestructor()) { if (dtor->isDeleted() || dtor->getAccess() != clang::AS_public) { return false; @@ -3376,7 +3387,7 @@ namespace { // Create the struct declaration and record it. auto name = importedName.getDeclName().getBaseIdentifier(); - StructDecl *result = nullptr; + NominalTypeDecl *result = nullptr; // Try to find an already-imported struct. This case happens any time // there are nested structs. The "Parent" struct will import the "Child" // struct at which point it attempts to import its decl context which is @@ -3386,9 +3397,15 @@ namespace { Impl.ImportedDecls.find({decl->getCanonicalDecl(), getVersion()}); if (alreadyImportedResult != Impl.ImportedDecls.end()) return alreadyImportedResult->second; - result = Impl.createDeclWithClangNode( - decl, AccessLevel::Public, Impl.importSourceLoc(decl->getBeginLoc()), - name, Impl.importSourceLoc(decl->getLocation()), None, nullptr, dc); + + auto loc = Impl.importSourceLoc(decl->getLocation()); + if (hasImportAsRefAttr(decl)) + result = Impl.createDeclWithClangNode( + decl, AccessLevel::Public, loc, name, loc, + ArrayRef{}, nullptr, dc, false); + else + result = Impl.createDeclWithClangNode( + decl, AccessLevel::Public, loc, name, loc, None, nullptr, dc); Impl.ImportedDecls[{decl->getCanonicalDecl(), getVersion()}] = result; // FIXME: Figure out what to do with superclasses in C++. One possible @@ -3548,16 +3565,23 @@ namespace { ctors.push_back(valueCtor); } - // Add ctors directly as they cannot always be looked up from the clang - // decl (some are synthesized by Swift). - for (auto ctor : ctors) { - result->addMember(ctor); + // Do not allow Swift to construct foreign reference types (at least, not + // yet). + if (isa(result)) { + for (auto ctor : ctors) { + // Add ctors directly as they cannot always be looked up from the + // clang decl (some are synthesized by Swift). + result->addMember(ctor); + } } - result->setHasUnreferenceableStorage(hasUnreferenceableStorage); + if (auto structResult = dyn_cast(result)) + structResult->setHasUnreferenceableStorage(hasUnreferenceableStorage); if (cxxRecordDecl) { - result->setIsCxxNonTrivial(!cxxRecordDecl->isTriviallyCopyable()); + if (auto structResult = dyn_cast(result)) + structResult->setIsCxxNonTrivial( + !cxxRecordDecl->isTriviallyCopyable()); for (auto &subscriptInfo : Impl.cxxSubscripts) { auto declAndParameterType = subscriptInfo.first; @@ -3871,6 +3895,26 @@ namespace { VarDecl *getImplicitProperty(ImportedName importedName, const clang::FunctionDecl *accessor); + static bool + foreignReferenceTypePassedByRef(const clang::FunctionDecl *decl) { + bool anyParamPassesByVal = + llvm::any_of(decl->parameters(), [](auto *param) { + if (auto recordType = dyn_cast( + param->getType().getCanonicalType())) + return hasImportAsRefAttr(recordType->getDecl()); + return false; + }); + + if (anyParamPassesByVal) + return true; + + if (auto recordType = dyn_cast( + decl->getReturnType().getCanonicalType())) + return hasImportAsRefAttr(recordType->getDecl()); + + return false; + } + Decl *VisitFunctionDecl(const clang::FunctionDecl *decl) { // Import the name of the function. ImportedName importedName; @@ -3879,6 +3923,11 @@ namespace { if (!importedName) return nullptr; + // Don't import functions that pass a foreign reference type by value + // (either as a parameter or return type). + if (foreignReferenceTypePassedByRef(decl)) + return nullptr; + AbstractStorageDecl *owningStorage; switch (importedName.getAccessorKind()) { case ImportedAccessorKind::None: @@ -4054,10 +4103,13 @@ namespace { selfIdx = None; } else { selfIdx = 0; + // Don't import members of a class decl as mutating. // If the method is imported as mutating, this implicitly makes the // parameter indirect. - selfIsInOut = Impl.SwiftContext.getClangModuleLoader() - ->isCXXMethodMutating(mdecl); + selfIsInOut = + !isa(dc) && + Impl.SwiftContext.getClangModuleLoader()->isCXXMethodMutating( + mdecl); } } } @@ -4214,6 +4266,14 @@ namespace { if (!dc) return nullptr; + // TODO: do we want to emit a diagnostic here? + // Types that are marked as foreign references cannot be stored by value. + if (auto recordType = + dyn_cast(decl->getType().getCanonicalType())) { + if (hasImportAsRefAttr(recordType->getDecl())) + return nullptr; + } + auto importedType = Impl.importType(decl->getType(), ImportTypeKind::RecordField, isInSystemModule(dc), Bridgeability::None); @@ -9759,7 +9819,7 @@ static void loadAllMembersOfSuperclassIfNeeded(ClassDecl *CD) { } void ClangImporter::Implementation::loadAllMembersOfRecordDecl( - StructDecl *recordDecl) { + NominalTypeDecl *recordDecl) { auto clangRecord = cast(recordDecl->getClangDecl()); // Import all of the members. @@ -9820,7 +9880,7 @@ ClangImporter::Implementation::loadAllMembers(Decl *D, uint64_t extra) { } if (isa_and_nonnull(D->getClangDecl())) { - loadAllMembersOfRecordDecl(cast(D)); + loadAllMembersOfRecordDecl(cast(D)); return; } diff --git a/lib/ClangImporter/ImportType.cpp b/lib/ClangImporter/ImportType.cpp index 9c60362c570a8..9540ef792e151 100644 --- a/lib/ClangImporter/ImportType.cpp +++ b/lib/ClangImporter/ImportType.cpp @@ -452,6 +452,10 @@ namespace { pointeeQualType, ImportTypeKind::Value, AllowNSUIntegerAsInt, Bridgeability::None); + // If this is imported as a reference type, ignore the pointer. + if (pointeeType && pointeeType->isForeignReferenceType()) + return {pointeeType, ImportHint::OtherPointer}; + // If the pointed-to type is unrepresentable in Swift, or its C // alignment is greater than the maximum Swift alignment, import as // OpaquePointer. @@ -524,6 +528,9 @@ namespace { if (!pointeeType) return Type(); + if (pointeeType->isForeignReferenceType()) + return {pointeeType, ImportHint::None}; + if (pointeeQualType->isFunctionType()) { return importFunctionPointerLikeType(*type, pointeeType); } @@ -1516,7 +1523,8 @@ static ImportedType adjustTypeForConcreteImport( assert(importedType); if (importKind == ImportTypeKind::RecordField && - importedType->isAnyClassReferenceType()) { + importedType->isAnyClassReferenceType() && + !importedType->isForeignReferenceType()) { // Wrap retainable struct fields in Unmanaged. // FIXME: Eventually we might get C++-like support for strong pointers in // structs, at which point we should really be checking the lifetime @@ -1957,8 +1965,11 @@ ParameterList *ClangImporter::Implementation::importFunctionParameterList( param, AccessLevel::Private, SourceLoc(), SourceLoc(), name, importSourceLoc(param->getLocation()), bodyName, ImportedHeaderUnit); - paramInfo->setSpecifier(isInOut ? ParamSpecifier::InOut - : ParamSpecifier::Default); + // Foreign references are already references so they don't need to be passed + // as inout. + paramInfo->setSpecifier(isInOut && !swiftParamTy->isForeignReferenceType() + ? ParamSpecifier::InOut + : ParamSpecifier::Default); paramInfo->setInterfaceType(swiftParamTy); recordImplicitUnwrapForDecl(paramInfo, isParamTypeImplicitlyUnwrapped); parameters.push_back(paramInfo); diff --git a/lib/ClangImporter/ImporterImpl.h b/lib/ClangImporter/ImporterImpl.h index 2b097ea9add97..91344de6d8b78 100644 --- a/lib/ClangImporter/ImporterImpl.h +++ b/lib/ClangImporter/ImporterImpl.h @@ -1390,7 +1390,7 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation void loadAllMembersOfObjcContainer(Decl *D, const clang::ObjCContainerDecl *objcContainer); - void loadAllMembersOfRecordDecl(StructDecl *recordDecl); + void loadAllMembersOfRecordDecl(NominalTypeDecl *recordDecl); void collectMembersToAdd(const clang::ObjCContainerDecl *objcContainer, Decl *D, DeclContext *DC, diff --git a/lib/IRGen/GenCall.cpp b/lib/IRGen/GenCall.cpp index 4dbcdeb177aee..fec6c954b3e60 100644 --- a/lib/IRGen/GenCall.cpp +++ b/lib/IRGen/GenCall.cpp @@ -1448,7 +1448,7 @@ void SignatureExpansion::expandExternalSignatureTypes() { auto paramTy = getSILFuncConventions().getSILType( param, IGM.getMaximalTypeExpansionContext()); auto ¶mTI = cast(IGM.getTypeInfo(paramTy)); - if (AI.getIndirectByVal()) { + if (AI.getIndirectByVal() && !paramTy.isForeignReferenceType()) { addByvalArgumentAttributes( IGM, Attrs, getCurParamIndex(), Alignment(AI.getIndirectAlign().getQuantity()), @@ -3531,6 +3531,20 @@ static void externalizeArguments(IRGenFunction &IGF, const Callee &callee, SILType paramType = silConv.getSILType( params[i - firstParam], IGF.IGM.getMaximalTypeExpansionContext()); + + // In Swift, values that are foreign references types will always be + // pointers. Additionally, we only import functions which use foreign + // reference types indirectly (as pointers), so we know in every case, if + // the argument type is a foreign reference type, the types will match up + // and we can simply use the input directly. + if (paramType.isForeignReferenceType()) { + auto *arg = in.claimNext(); + if (isIndirectFormalParameter(params[i - firstParam].getConvention())) + arg = IGF.Builder.CreateLoad(arg, IGF.IGM.getPointerAlignment()); + out.add(arg); + continue; + } + switch (AI.getKind()) { case clang::CodeGen::ABIArgInfo::Extend: { bool signExt = clangParamTy->hasSignedIntegerRepresentation(); diff --git a/lib/IRGen/GenClass.cpp b/lib/IRGen/GenClass.cpp index 2e085c80df0aa..263dfa23edc00 100644 --- a/lib/IRGen/GenClass.cpp +++ b/lib/IRGen/GenClass.cpp @@ -147,6 +147,8 @@ namespace { addNSObjectHeader(); HeaderSize = CurSize; break; + case ReferenceCounting::None: + break; case ReferenceCounting::Block: case ReferenceCounting::Unknown: case ReferenceCounting::Bridge: @@ -215,7 +217,7 @@ namespace { void addFieldsForClassImpl(ClassDecl *rootClass, SILType rootClassType, ClassDecl *theClass, SILType classType, bool superclass) { - if (theClass->hasClangNode()) { + if (theClass->hasClangNode() && !theClass->isForeignReferenceType()) { Options |= ClassMetadataFlags::ClassHasObjCAncestry; return; } diff --git a/lib/IRGen/GenDecl.cpp b/lib/IRGen/GenDecl.cpp index 8dc2cfd6ead6f..a445f3c2ee11a 100644 --- a/lib/IRGen/GenDecl.cpp +++ b/lib/IRGen/GenDecl.cpp @@ -3663,7 +3663,7 @@ IRGenModule::getTypeEntityReference(GenericTypeDecl *decl) { if (auto nominal = dyn_cast(decl)) { auto clas = dyn_cast(decl); - if (!clas) { + if (!clas || clas->isForeignReferenceType()) { return getTypeContextDescriptorEntityReference(*this, nominal); } diff --git a/lib/IRGen/GenHeap.cpp b/lib/IRGen/GenHeap.cpp index 4f6a27b1fe32e..c880519d634d6 100644 --- a/lib/IRGen/GenHeap.cpp +++ b/lib/IRGen/GenHeap.cpp @@ -584,6 +584,7 @@ unsigned IRGenModule::getReferenceStorageExtraInhabitantCount( return getHeapObjectExtraInhabitantCount(*this); case ReferenceCounting::Block: case ReferenceCounting::ObjC: + case ReferenceCounting::None: case ReferenceCounting::Unknown: break; case ReferenceCounting::Bridge: @@ -613,6 +614,7 @@ SpareBitVector IRGenModule::getReferenceStorageSpareBits( return getHeapObjectSpareBits(); case ReferenceCounting::Block: case ReferenceCounting::ObjC: + case ReferenceCounting::None: case ReferenceCounting::Unknown: break; case ReferenceCounting::Bridge: @@ -642,6 +644,7 @@ APInt IRGenModule::getReferenceStorageExtraInhabitantValue(unsigned bits, return getHeapObjectFixedExtraInhabitantValue(*this, bits, index, 0); case ReferenceCounting::Block: case ReferenceCounting::ObjC: + case ReferenceCounting::None: case ReferenceCounting::Unknown: break; case ReferenceCounting::Bridge: @@ -662,6 +665,7 @@ APInt IRGenModule::getReferenceStorageExtraInhabitantMask( case ReferenceCounting::Native: case ReferenceCounting::Block: case ReferenceCounting::ObjC: + case ReferenceCounting::None: case ReferenceCounting::Unknown: break; case ReferenceCounting::Bridge: @@ -681,6 +685,7 @@ llvm::Value *IRGenFunction::getReferenceStorageExtraInhabitantIndex(Address src, return getHeapObjectExtraInhabitantIndex(*this, src); case ReferenceCounting::Block: case ReferenceCounting::ObjC: + case ReferenceCounting::None: case ReferenceCounting::Unknown: break; case ReferenceCounting::Bridge: @@ -717,6 +722,7 @@ void IRGenFunction::storeReferenceStorageExtraInhabitant(llvm::Value *index, return storeHeapObjectExtraInhabitant(*this, index, dest); case ReferenceCounting::Block: case ReferenceCounting::ObjC: + case ReferenceCounting::None: case ReferenceCounting::Unknown: break; case ReferenceCounting::Bridge: @@ -738,35 +744,30 @@ void IRGenFunction::storeReferenceStorageExtraInhabitant(llvm::Value *index, Builder.CreateStore(null, dest); } -#define SOMETIMES_UNKNOWN(Name) \ - const TypeInfo * \ - TypeConverter::create##Name##StorageType(llvm::Type *valueType, \ - ReferenceCounting style, \ - bool isOptional) { \ - auto &&spareBits = IGM.getReferenceStorageSpareBits( \ - ReferenceOwnership::Name, style); \ - switch (style) { \ - case ReferenceCounting::Native: \ - return new Native##Name##ReferenceTypeInfo(valueType, \ - IGM.Name##ReferencePtrTy->getElementType(), \ - IGM.getPointerSize(), \ - IGM.getPointerAlignment(), \ - std::move(spareBits), \ - isOptional); \ - case ReferenceCounting::ObjC: \ - case ReferenceCounting::Block: \ - case ReferenceCounting::Unknown: \ - return new Unknown##Name##ReferenceTypeInfo(valueType, \ - IGM.Name##ReferencePtrTy->getElementType(), \ - IGM.getPointerSize(), \ - IGM.getPointerAlignment(), \ - std::move(spareBits), \ - isOptional); \ - case ReferenceCounting::Bridge: \ - case ReferenceCounting::Error: \ - llvm_unreachable("not supported!"); \ - } \ - llvm_unreachable("bad reference-counting style"); \ +#define SOMETIMES_UNKNOWN(Name) \ + const TypeInfo *TypeConverter::create##Name##StorageType( \ + llvm::Type *valueType, ReferenceCounting style, bool isOptional) { \ + auto &&spareBits = \ + IGM.getReferenceStorageSpareBits(ReferenceOwnership::Name, style); \ + switch (style) { \ + case ReferenceCounting::Native: \ + return new Native##Name##ReferenceTypeInfo( \ + valueType, IGM.Name##ReferencePtrTy->getElementType(), \ + IGM.getPointerSize(), IGM.getPointerAlignment(), \ + std::move(spareBits), isOptional); \ + case ReferenceCounting::ObjC: \ + case ReferenceCounting::None: \ + case ReferenceCounting::Block: \ + case ReferenceCounting::Unknown: \ + return new Unknown##Name##ReferenceTypeInfo( \ + valueType, IGM.Name##ReferencePtrTy->getElementType(), \ + IGM.getPointerSize(), IGM.getPointerAlignment(), \ + std::move(spareBits), isOptional); \ + case ReferenceCounting::Bridge: \ + case ReferenceCounting::Error: \ + llvm_unreachable("not supported!"); \ + } \ + llvm_unreachable("bad reference-counting style"); \ } #define ALWAYS_NATIVE(Name) \ const TypeInfo * \ @@ -1011,6 +1012,8 @@ void IRGenFunction::emitStrongRelease(llvm::Value *value, return emitBridgeStrongRelease(value, atomicity); case ReferenceCounting::Error: return emitErrorStrongRelease(value); + case ReferenceCounting::None: + return; // This is a no-op if we don't have any ref-counting. } } @@ -1036,6 +1039,8 @@ void IRGenFunction::emitStrongRetain(llvm::Value *value, case ReferenceCounting::Error: emitErrorStrongRetain(value); return; + case ReferenceCounting::None: + return; // This is a no-op if we don't have any ref-counting. } } @@ -1053,43 +1058,47 @@ llvm::Type *IRGenModule::getReferenceType(ReferenceCounting refcounting) { return UnknownRefCountedPtrTy; case ReferenceCounting::Error: return ErrorPtrTy; + case ReferenceCounting::None: + return OpaquePtrTy; } llvm_unreachable("Not a valid ReferenceCounting."); } #define DEFINE_BINARY_OPERATION(KIND, RESULT, TYPE1, TYPE2) \ -RESULT IRGenFunction::emit##KIND(TYPE1 val1, TYPE2 val2, \ - ReferenceCounting style) { \ - switch (style) { \ - case ReferenceCounting::Native: \ - return emitNative##KIND(val1, val2); \ - case ReferenceCounting::ObjC: \ - case ReferenceCounting::Unknown: \ - return emitUnknown##KIND(val1, val2); \ - case ReferenceCounting::Bridge: \ - case ReferenceCounting::Block: \ - case ReferenceCounting::Error: \ - llvm_unreachable("unsupported reference kind with reference storage"); \ - } \ - llvm_unreachable("bad refcounting style"); \ -} + RESULT IRGenFunction::emit##KIND(TYPE1 val1, TYPE2 val2, \ + ReferenceCounting style) { \ + switch (style) { \ + case ReferenceCounting::Native: \ + return emitNative##KIND(val1, val2); \ + case ReferenceCounting::ObjC: \ + case ReferenceCounting::Unknown: \ + return emitUnknown##KIND(val1, val2); \ + case ReferenceCounting::Bridge: \ + case ReferenceCounting::Block: \ + case ReferenceCounting::Error: \ + case ReferenceCounting::None: \ + llvm_unreachable("unsupported reference kind with reference storage"); \ + } \ + llvm_unreachable("bad refcounting style"); \ + } #define DEFINE_UNARY_OPERATION(KIND, RESULT, TYPE1) \ -RESULT IRGenFunction::emit##KIND(TYPE1 val1, ReferenceCounting style) { \ - switch (style) { \ - case ReferenceCounting::Native: \ - return emitNative##KIND(val1); \ - case ReferenceCounting::ObjC: \ - case ReferenceCounting::Unknown: \ - return emitUnknown##KIND(val1); \ - case ReferenceCounting::Bridge: \ - case ReferenceCounting::Block: \ - case ReferenceCounting::Error: \ - llvm_unreachable("unsupported reference kind with reference storage"); \ - } \ - llvm_unreachable("bad refcounting style"); \ -} + RESULT IRGenFunction::emit##KIND(TYPE1 val1, ReferenceCounting style) { \ + switch (style) { \ + case ReferenceCounting::Native: \ + return emitNative##KIND(val1); \ + case ReferenceCounting::ObjC: \ + case ReferenceCounting::Unknown: \ + return emitUnknown##KIND(val1); \ + case ReferenceCounting::Bridge: \ + case ReferenceCounting::Block: \ + case ReferenceCounting::Error: \ + case ReferenceCounting::None: \ + llvm_unreachable("unsupported reference kind with reference storage"); \ + } \ + llvm_unreachable("bad refcounting style"); \ + } #define NEVER_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ DEFINE_BINARY_OPERATION(Name##CopyInit, void, Address, Address) \ diff --git a/lib/IRGen/GenMeta.cpp b/lib/IRGen/GenMeta.cpp index 059467a14f2fa..00267c13db09d 100644 --- a/lib/IRGen/GenMeta.cpp +++ b/lib/IRGen/GenMeta.cpp @@ -23,6 +23,7 @@ #include "swift/AST/Attr.h" #include "swift/AST/CanTypeVisitor.h" #include "swift/AST/Decl.h" +#include "swift/AST/DiagnosticsIRGen.h" #include "swift/AST/GenericEnvironment.h" #include "swift/AST/IRGenOptions.h" #include "swift/AST/PrettyStackTrace.h" @@ -1624,7 +1625,8 @@ namespace { VTable(IGM.getSILModule().lookUpVTable(getType())), Resilient(IGM.hasResilientMetadata(Type, ResilienceExpansion::Minimal)) { - if (getType()->isForeign()) return; + if (getType()->isForeign() || Type->isForeignReferenceType()) + return; MetadataLayout = &IGM.getClassMetadataLayout(Type); @@ -1654,6 +1656,7 @@ namespace { } void layout() { + assert(!getType()->isForeignReferenceType()); super::layout(); addVTable(); addOverrideTable(); @@ -2830,6 +2833,13 @@ 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); + } + // Otherwise, we know the offset at compile time, even if our // clients do not, so just emit a constant. auto &layout = IGM.getClassMetadataLayout(classDecl); @@ -5030,6 +5040,8 @@ namespace { void emitInitializeMetadata(IRGenFunction &IGF, llvm::Value *metadata, MetadataDependencyCollector *collector) { + assert(!getTargetType()->isForeignReferenceType()); + if (!Target->hasSuperclass()) { assert(IGM.getOptions().LazyInitializeClassMetadata && "should have superclass if not lazy initializing class metadata"); @@ -5053,6 +5065,8 @@ namespace { // Visitor methods. void addValueWitnessTable() { + assert(!getTargetType()->isForeignReferenceType()); + // The runtime will fill in the default VWT during allocation for the // foreign class metadata. // @@ -5081,6 +5095,7 @@ namespace { } void addMetadataFlags() { + assert(!getTargetType()->isForeignReferenceType()); B.addInt(IGM.MetadataKindTy, (unsigned) MetadataKind::ForeignClass); } @@ -5173,6 +5188,8 @@ bool irgen::requiresForeignTypeMetadata(CanType type) { bool irgen::requiresForeignTypeMetadata(NominalTypeDecl *decl) { if (auto *clas = dyn_cast(decl)) { + assert(!clas->isForeignReferenceType()); + switch (clas->getForeignClassKind()) { case ClassDecl::ForeignKind::Normal: case ClassDecl::ForeignKind::RuntimeOnly: @@ -5196,7 +5213,8 @@ void irgen::emitForeignTypeMetadata(IRGenModule &IGM, NominalTypeDecl *decl) { init.setPacked(true); if (auto classDecl = dyn_cast(decl)) { - assert(classDecl->getForeignClassKind() == ClassDecl::ForeignKind::CFType); + assert(classDecl->getForeignClassKind() == ClassDecl::ForeignKind::CFType || + classDecl->isForeignReferenceType()); ForeignClassMetadataBuilder builder(IGM, classDecl, init); builder.layout(); diff --git a/lib/IRGen/GenProto.cpp b/lib/IRGen/GenProto.cpp index 836a354c993a9..f7faa8febd0da 100644 --- a/lib/IRGen/GenProto.cpp +++ b/lib/IRGen/GenProto.cpp @@ -31,6 +31,7 @@ #include "swift/AST/CanTypeVisitor.h" #include "swift/AST/Types.h" #include "swift/AST/Decl.h" +#include "swift/AST/DiagnosticsIRGen.h" #include "swift/AST/GenericEnvironment.h" #include "swift/AST/LazyResolver.h" #include "swift/AST/IRGenOptions.h" @@ -1492,6 +1493,15 @@ 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"); diff --git a/lib/IRGen/GenReflection.cpp b/lib/IRGen/GenReflection.cpp index 98ab5b6a1806d..6f116d25c4e4b 100644 --- a/lib/IRGen/GenReflection.cpp +++ b/lib/IRGen/GenReflection.cpp @@ -15,6 +15,7 @@ //===----------------------------------------------------------------------===// #include "swift/AST/Decl.h" +#include "swift/AST/DiagnosticsIRGen.h" #include "swift/AST/GenericEnvironment.h" #include "swift/AST/IRGenOptions.h" #include "swift/AST/PrettyStackTrace.h" @@ -560,6 +561,8 @@ class ReflectionMetadataBuilder { CanGenericSignature sig, MangledTypeRefRole role = MangledTypeRefRole::Reflection) { + assert(!type->isForeignReferenceType()); + B.addRelativeAddress(IGM.getTypeRef(type, sig, role).first); addBuiltinTypeRefs(type); } @@ -707,6 +710,15 @@ class FieldTypeMetadataBuilder : public ReflectionMetadataBuilder { if (!type) { B.addInt32(0); } else { + if (type->isForeignReferenceType()) { + type->getASTContext().Diags.diagnose( + type->lookThroughAllOptionalTypes() + ->getClassOrBoundGenericClass() + ->getLoc(), + diag::foreign_reference_types_unsupported.ID, {}); + exit(1); + } + auto genericSig = NTD->getGenericSignature(); // The standard library's Mirror demangles metadata from field diff --git a/lib/IRGen/GenType.cpp b/lib/IRGen/GenType.cpp index 5557b6aeff8d9..a521f8de7d7f8 100644 --- a/lib/IRGen/GenType.cpp +++ b/lib/IRGen/GenType.cpp @@ -1594,6 +1594,7 @@ IRGenModule::getReferenceObjectTypeInfo(ReferenceCounting refcounting) { case ReferenceCounting::Block: case ReferenceCounting::Error: case ReferenceCounting::ObjC: + case ReferenceCounting::None: llvm_unreachable("not implemented"); } diff --git a/lib/IRGen/GenValueWitness.cpp b/lib/IRGen/GenValueWitness.cpp index 257b7692c0a7e..8c091b3ce439f 100644 --- a/lib/IRGen/GenValueWitness.cpp +++ b/lib/IRGen/GenValueWitness.cpp @@ -1162,6 +1162,7 @@ getAddrOfKnownValueWitnessTable(IRGenModule &IGM, CanType type, witnessSurrogate = C.TheBridgeObjectType; break; case ReferenceCounting::Error: + case ReferenceCounting::None: break; } } diff --git a/lib/IRGen/IRGenMangler.cpp b/lib/IRGen/IRGenMangler.cpp index 56a7e68633ec2..ea92243bb26b8 100644 --- a/lib/IRGen/IRGenMangler.cpp +++ b/lib/IRGen/IRGenMangler.cpp @@ -122,7 +122,8 @@ IRGenMangler::withSymbolicReferences(IRGenModule &IGM, return true; // Foreign class types can be symbolically referenced. - if (clas->getForeignClassKind() == ClassDecl::ForeignKind::CFType) + if (clas->getForeignClassKind() == ClassDecl::ForeignKind::CFType || + const_cast(clas)->isForeignReferenceType()) return true; // Otherwise no. diff --git a/lib/IRGen/IRGenModule.h b/lib/IRGen/IRGenModule.h index 6d7f6357a79fe..e7be1f177a09c 100644 --- a/lib/IRGen/IRGenModule.h +++ b/lib/IRGen/IRGenModule.h @@ -823,6 +823,7 @@ class IRGenModule { case ReferenceCounting::Unknown: case ReferenceCounting::ObjC: case ReferenceCounting::Block: + case ReferenceCounting::None: return true; case ReferenceCounting::Bridge: diff --git a/lib/IRGen/IRGenSIL.cpp b/lib/IRGen/IRGenSIL.cpp index 245e2aae82efc..91faf970d399c 100644 --- a/lib/IRGen/IRGenSIL.cpp +++ b/lib/IRGen/IRGenSIL.cpp @@ -2135,7 +2135,10 @@ static void emitDynamicSelfMetadata(IRGenSILFunction &IGF) { selfTy = metaTy.getInstanceType(); switch (metaTy->getRepresentation()) { case MetatypeRepresentation::Thin: - llvm_unreachable("class metatypes are never thin"); + assert(selfTy.isForeignReferenceType() && + "Only foreign reference metatypes are allowed to be thin"); + selfKind = IRGenFunction::ObjectReference; + break; case MetatypeRepresentation::Thick: selfKind = IRGenFunction::SwiftMetatype; break; diff --git a/lib/IRGen/MetadataLayout.cpp b/lib/IRGen/MetadataLayout.cpp index d136f1131a751..35ea3242a23f2 100644 --- a/lib/IRGen/MetadataLayout.cpp +++ b/lib/IRGen/MetadataLayout.cpp @@ -112,7 +112,8 @@ MetadataLayout &IRGenModule::getMetadataLayout(NominalTypeDecl *decl) { auto &entry = MetadataLayouts[decl]; if (!entry) { if (auto theClass = dyn_cast(decl)) { - if (theClass->getForeignClassKind() == ClassDecl::ForeignKind::CFType) + if (theClass->getForeignClassKind() == ClassDecl::ForeignKind::CFType || + theClass->isForeignReferenceType()) entry = new ForeignClassMetadataLayout(*this, theClass); else entry = new ClassMetadataLayout(*this, theClass); @@ -480,7 +481,8 @@ ClassMetadataLayout::getFieldOffsetVectorOffset(IRGenFunction &IGF) const { Size irgen::getClassFieldOffsetOffset(IRGenModule &IGM, ClassDecl *theClass, VarDecl *field) { - if (theClass->getForeignClassKind() == ClassDecl::ForeignKind::CFType) + if (theClass->getForeignClassKind() == ClassDecl::ForeignKind::CFType || + theClass->isForeignReferenceType()) return Size(); return IGM.getClassMetadataLayout(theClass).getStaticFieldOffset(field); @@ -679,7 +681,8 @@ StructMetadataLayout::getTrailingFlagsOffset() const { ForeignClassMetadataLayout::ForeignClassMetadataLayout(IRGenModule &IGM, ClassDecl *theClass) : MetadataLayout(Kind::ForeignClass), Class(theClass) { - assert(theClass->getForeignClassKind() == ClassDecl::ForeignKind::CFType && + assert(theClass->getForeignClassKind() == ClassDecl::ForeignKind::CFType || + theClass->isForeignReferenceType() && "Not a foreign class"); struct Scanner : LayoutScanner { diff --git a/lib/IRGen/MetadataRequest.cpp b/lib/IRGen/MetadataRequest.cpp index 21ff16f54e6c4..8074ef9840fac 100644 --- a/lib/IRGen/MetadataRequest.cpp +++ b/lib/IRGen/MetadataRequest.cpp @@ -34,6 +34,7 @@ #include "IRGenModule.h" #include "swift/AST/ASTContext.h" #include "swift/AST/CanTypeVisitor.h" +#include "swift/AST/DiagnosticsIRGen.h" #include "swift/AST/ExistentialLayout.h" #include "swift/AST/GenericEnvironment.h" #include "swift/AST/IRGenOptions.h" @@ -2860,6 +2861,15 @@ 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); @@ -3249,6 +3259,9 @@ namespace { case ReferenceCounting::Bridge: case ReferenceCounting::Error: llvm_unreachable("classes shouldn't have this kind of refcounting"); + case ReferenceCounting::None: + llvm_unreachable( + "Foreign reference types don't conform to 'AnyClass'."); } llvm_unreachable("Not a valid ReferenceCounting."); diff --git a/lib/PrintAsObjC/DeclAndTypePrinter.cpp b/lib/PrintAsObjC/DeclAndTypePrinter.cpp index 98803ea8e88a8..4bdd547483b21 100644 --- a/lib/PrintAsObjC/DeclAndTypePrinter.cpp +++ b/lib/PrintAsObjC/DeclAndTypePrinter.cpp @@ -1028,6 +1028,9 @@ class DeclAndTypePrinter::Implementation /// annotations like \c strong and \c weak. bool isObjCReferenceCountableObjectType(Type ty) { if (auto classDecl = ty->getClassOrBoundGenericClass()) { + if (classDecl->isForeignReferenceType()) + return false; + switch (classDecl->getForeignClassKind()) { case ClassDecl::ForeignKind::Normal: case ClassDecl::ForeignKind::RuntimeOnly: diff --git a/lib/SIL/IR/SILFunctionType.cpp b/lib/SIL/IR/SILFunctionType.cpp index 474f204bbd47b..723df268efd7e 100644 --- a/lib/SIL/IR/SILFunctionType.cpp +++ b/lib/SIL/IR/SILFunctionType.cpp @@ -2599,6 +2599,8 @@ class CFunctionTypeConventions : public Conventions { return ResultConvention::Unowned; if (FnType->getExtInfo().getProducesResult()) return ResultConvention::Owned; + if (tl.getLoweredType().isForeignReferenceType()) + return ResultConvention::Unowned; return ResultConvention::Autoreleased; } @@ -3105,14 +3107,17 @@ CanSILFunctionType TypeConverter::getUncachedSILFunctionTypeForConstant( bridgedTypes); } -static bool isClassOrProtocolMethod(ValueDecl *vd) { +static bool isObjCMethod(ValueDecl *vd) { if (!vd->getDeclContext()) return false; + Type contextType = vd->getDeclContext()->getDeclaredInterfaceType(); if (!contextType) return false; - return contextType->getClassOrBoundGenericClass() - || contextType->isClassExistentialType(); + + bool isRefCountedClass = contextType->getClassOrBoundGenericClass() && + !contextType->isForeignReferenceType(); + return isRefCountedClass || contextType->isClassExistentialType(); } SILFunctionTypeRepresentation @@ -3123,7 +3128,7 @@ TypeConverter::getDeclRefRepresentation(SILDeclRef c) { c.getDecl()->isImportAsMember()) return SILFunctionTypeRepresentation::CFunctionPointer; - if (isClassOrProtocolMethod(c.getDecl()) || + if (isObjCMethod(c.getDecl()) || c.kind == SILDeclRef::Kind::IVarInitializer || c.kind == SILDeclRef::Kind::IVarDestroyer) return SILFunctionTypeRepresentation::ObjCMethod; diff --git a/lib/SIL/IR/TypeLowering.cpp b/lib/SIL/IR/TypeLowering.cpp index 5a603962fcaf7..d0d43aa2f64e4 100644 --- a/lib/SIL/IR/TypeLowering.cpp +++ b/lib/SIL/IR/TypeLowering.cpp @@ -51,6 +51,8 @@ namespace { /// Class metatypes have non-trivial representation due to the /// possibility of subclassing. bool visitClassType(CanClassType type) { + if (type->isForeignReferenceType()) + return true; return false; } bool visitBoundGenericClassType(CanBoundGenericClassType type) { @@ -1651,6 +1653,10 @@ namespace { TypeLowering *handleReference(CanType type, RecursiveProperties properties) { auto silType = SILType::getPrimitiveObjectType(type); + if (type.isForeignReferenceType()) + return new (TC) TrivialTypeLowering( + silType, RecursiveProperties::forTrivial(), Expansion); + return new (TC) ReferenceTypeLowering(silType, properties, Expansion); } diff --git a/lib/SIL/Utils/InstructionUtils.cpp b/lib/SIL/Utils/InstructionUtils.cpp index 6068e4d97e297..1ef291d636913 100644 --- a/lib/SIL/Utils/InstructionUtils.cpp +++ b/lib/SIL/Utils/InstructionUtils.cpp @@ -660,8 +660,10 @@ RuntimeEffect swift::getRuntimeEffect(SILInstruction *inst, SILType &impactType) if (auto selfType = instTy->getAs()) instTy = selfType->getSelfType(); auto *cl = instTy->getClassOrBoundGenericClass(); - if ((cl && (cl->usesObjCObjectModel() || cl->isForeign())) || - instTy->isAnyObject()) + bool isForeign = + cl->getObjectModel() == ReferenceCounting::ObjC || + cl->isForeign(); + if ((cl && isForeign) || instTy->isAnyObject()) return RuntimeEffect::MetaData | RuntimeEffect::ObjectiveC; return RuntimeEffect::MetaData; } @@ -678,7 +680,8 @@ RuntimeEffect swift::getRuntimeEffect(SILInstruction *inst, SILType &impactType) return RuntimeEffect::MetaData; case ExistentialRepresentation::Class: { auto *cl = opType.getClassOrBoundGenericClass(); - if ((cl && cl->usesObjCObjectModel()) || opType.isAnyObject()) + bool usesObjCModel = cl->getObjectModel() == ReferenceCounting::ObjC; + if ((cl && usesObjCModel) || opType.isAnyObject()) return RuntimeEffect::MetaData | RuntimeEffect::ObjectiveC; return RuntimeEffect::MetaData | RuntimeEffect::ObjectiveC; } diff --git a/lib/SIL/Verifier/SILVerifier.cpp b/lib/SIL/Verifier/SILVerifier.cpp index 17321fd940b31..9ad56036ac6ae 100644 --- a/lib/SIL/Verifier/SILVerifier.cpp +++ b/lib/SIL/Verifier/SILVerifier.cpp @@ -821,8 +821,10 @@ class SILVerifier : public SILVerifierBase { // Require that the operand is a non-optional, non-unowned reference-counted // type. void requireReferenceValue(SILValue value, const Twine &valueDescription) { - require(value->getType().isObject(), valueDescription +" must be an object"); - require(value->getType().isReferenceCounted(F.getModule()), + require(value->getType().isObject(), + valueDescription + " must be an object"); + require(value->getType().isReferenceCounted(F.getModule()) || + value->getType().isForeignReferenceType(), valueDescription + " must have reference semantics"); forbidObjectType(UnownedStorageType, value, valueDescription); } @@ -3430,9 +3432,11 @@ class SILVerifier : public SILVerifierBase { "foreign method cannot be dispatched natively"); require(!isa(member.getDecl()->getDeclContext()), "extension method cannot be dispatched natively"); - - // The method ought to appear in the class vtable. - require(VerifyClassMethodVisitor(member).Seen, + + // The method ought to appear in the class vtable unless it's a foreign + // reference type. + require(VerifyClassMethodVisitor(member).Seen || + operandType.isForeignReferenceType(), "method does not appear in the class's vtable"); } diff --git a/lib/SILGen/SILGenConstructor.cpp b/lib/SILGen/SILGenConstructor.cpp index 959be60d01f5f..3f1125fad66d3 100644 --- a/lib/SILGen/SILGenConstructor.cpp +++ b/lib/SILGen/SILGenConstructor.cpp @@ -570,7 +570,7 @@ void SILGenFunction::emitEnumConstructor(EnumElementDecl *element) { bool Lowering::usesObjCAllocator(ClassDecl *theClass) { // If the root class was implemented in Objective-C, use Objective-C's // allocation methods because they may have been overridden. - return theClass->usesObjCObjectModel(); + return theClass->getObjectModel() == ReferenceCounting::ObjC; } void SILGenFunction::emitClassConstructorAllocator(ConstructorDecl *ctor) { diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index 4725f3cdb6164..4110d527b8b60 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -5859,6 +5859,10 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind, type1, type2, locator); return getTypeMatchSuccess(); }; + + // Foreign reference types do *not* conform to AnyObject. + if (type1->isForeignReferenceType() && type2->isAnyObject()) + return getTypeMatchFailure(locator); if (auto meta1 = type1->getAs()) { if (meta1->getInstanceType()->mayHaveSuperclass() diff --git a/lib/Sema/TypeCheckStorage.cpp b/lib/Sema/TypeCheckStorage.cpp index 7091b56d26c80..390b24c07ad4b 100644 --- a/lib/Sema/TypeCheckStorage.cpp +++ b/lib/Sema/TypeCheckStorage.cpp @@ -97,8 +97,12 @@ static bool contextAllowsPatternBindingWithoutVariables(DeclContext *dc) { } static bool hasStoredProperties(NominalTypeDecl *decl) { + bool isForeignReferenceTy = + isa(decl) && cast(decl)->isForeignReferenceType(); + return (isa(decl) || - (isa(decl) && !decl->hasClangNode())); + (isa(decl) && + (!decl->hasClangNode() || isForeignReferenceTy))); } static void computeLoweredStoredProperties(NominalTypeDecl *decl) { @@ -2264,6 +2268,9 @@ RequiresOpaqueAccessorsRequest::evaluate(Evaluator &evaluator, } else if (auto *structDecl = dyn_cast(dc)) { if (structDecl->hasClangNode()) return false; + } else if (isa(dc) && + cast(dc)->isForeignReferenceType()) { + return false; } // Stored properties in SIL mode don't get accessors. diff --git a/test/Interop/Cxx/foreign-reference/Inputs/module.modulemap b/test/Interop/Cxx/foreign-reference/Inputs/module.modulemap new file mode 100644 index 0000000000000..fa71e36cb6c92 --- /dev/null +++ b/test/Interop/Cxx/foreign-reference/Inputs/module.modulemap @@ -0,0 +1,24 @@ +module POD { + header "pod.h" + requires cplusplus +} + +module MoveOnly { + header "move-only.h" + requires cplusplus +} + +module Singleton { + header "singleton.h" + requires cplusplus +} + +module Nullable { + header "nullable.h" + requires cplusplus +} + +module WitnessTable { + header "witness-table.h" + requires cplusplus +} diff --git a/test/Interop/Cxx/foreign-reference/Inputs/move-only.h b/test/Interop/Cxx/foreign-reference/Inputs/move-only.h new file mode 100644 index 0000000000000..49e3f24d7c145 --- /dev/null +++ b/test/Interop/Cxx/foreign-reference/Inputs/move-only.h @@ -0,0 +1,76 @@ +#ifndef TEST_INTEROP_CXX_FOREIGN_REFERENCE_INPUTS_MOVE_ONLY_H +#define TEST_INTEROP_CXX_FOREIGN_REFERENCE_INPUTS_MOVE_ONLY_H + +#include + +#include "visibility.h" + +inline void *operator new(size_t, void *p) { return p; } + +template +_Tp &&move(_Tp &t) { + return static_cast<_Tp &&>(t); +} + +SWIFT_BEGIN_NULLABILITY_ANNOTATIONS + +struct __attribute__((swift_attr("import_as_ref"))) MoveOnly { + MoveOnly() = default; + MoveOnly(const MoveOnly &) = delete; + MoveOnly(MoveOnly &&) = default; + + int test() const { return 42; } + int testMutable() { return 42; } + + static MoveOnly *create() { + return new (malloc(sizeof(MoveOnly))) MoveOnly(); + } +}; + +MoveOnly moveIntoResult(MoveOnly &x) { return move(x); } + +struct __attribute__((swift_attr("import_as_ref"))) HasMoveOnlyChild { + MoveOnly child; + + static HasMoveOnlyChild *create() { + return new (malloc(sizeof(HasMoveOnlyChild))) HasMoveOnlyChild(); + } +}; + +HasMoveOnlyChild moveIntoResult(HasMoveOnlyChild &x) { return move(x); } + +struct __attribute__((swift_attr("import_as_ref"))) PrivateCopyCtor { + PrivateCopyCtor() = default; + PrivateCopyCtor(PrivateCopyCtor &&) = default; + + int test() const { return 42; } + int testMutable() { return 42; } + + static PrivateCopyCtor *create() { + return new (malloc(sizeof(PrivateCopyCtor))) PrivateCopyCtor(); + } + +private: + PrivateCopyCtor(const PrivateCopyCtor &) {} +}; + +PrivateCopyCtor moveIntoResult(PrivateCopyCtor &x) { return move(x); } + +struct __attribute__((swift_attr("import_as_ref"))) BadCopyCtor { + BadCopyCtor() = default; + BadCopyCtor(BadCopyCtor &&) = default; + BadCopyCtor(const BadCopyCtor &) { __builtin_trap(); } + + int test() const { return 42; } + int testMutable() { return 42; } + + static BadCopyCtor *create() { + return new (malloc(sizeof(BadCopyCtor))) BadCopyCtor(); + } +}; + +BadCopyCtor moveIntoResult(BadCopyCtor &x) { return move(x); } + +SWIFT_END_NULLABILITY_ANNOTATIONS + +#endif // TEST_INTEROP_CXX_FOREIGN_REFERENCE_INPUTS_MOVE_ONLY_H diff --git a/test/Interop/Cxx/foreign-reference/Inputs/nullable.h b/test/Interop/Cxx/foreign-reference/Inputs/nullable.h new file mode 100644 index 0000000000000..372aae166aa95 --- /dev/null +++ b/test/Interop/Cxx/foreign-reference/Inputs/nullable.h @@ -0,0 +1,30 @@ +#ifndef TEST_INTEROP_CXX_FOREIGN_REFERENCE_INPUTS_NULLABLE_H +#define TEST_INTEROP_CXX_FOREIGN_REFERENCE_INPUTS_NULLABLE_H + +#include + +inline void *operator new(size_t, void *p) { return p; } + +struct __attribute__((swift_attr("import_as_ref"))) Empty { + int test() const { return 42; } + + static Empty *create() { return new (malloc(sizeof(Empty))) Empty(); } +}; + +void mutateIt(Empty &) {} + +struct __attribute__((swift_attr("import_as_ref"))) IntPair { + int a = 1; + int b = 2; + + int test() const { return b - a; } + + static IntPair *create() { return new (malloc(sizeof(IntPair))) IntPair(); } +}; + +void mutateIt(IntPair *x) { + x->a = 2; + x->b = 4; +} + +#endif // TEST_INTEROP_CXX_FOREIGN_REFERENCE_INPUTS_NULLABLE_H diff --git a/test/Interop/Cxx/foreign-reference/Inputs/pod.h b/test/Interop/Cxx/foreign-reference/Inputs/pod.h new file mode 100644 index 0000000000000..d0f2933d5cf84 --- /dev/null +++ b/test/Interop/Cxx/foreign-reference/Inputs/pod.h @@ -0,0 +1,122 @@ +#ifndef TEST_INTEROP_CXX_FOREIGN_REFERENCE_INPUTS_POD_H +#define TEST_INTEROP_CXX_FOREIGN_REFERENCE_INPUTS_POD_H + +#include + +#include "visibility.h" + +inline void *operator new(size_t, void *p) { return p; } + +SWIFT_BEGIN_NULLABILITY_ANNOTATIONS + +struct __attribute__((swift_attr("import_as_ref"))) Empty { + int test() const { return 42; } + int testMutable() { return 42; } + + static Empty *create() { return new (malloc(sizeof(Empty))) Empty(); } +}; + +void mutateIt(Empty &) {} +Empty passThroughByValue(Empty x) { return x; } + +struct __attribute__((swift_attr("@actor"))) +__attribute__((swift_attr("import_as_ref"))) MultipleAttrs { + int test() const { return 42; } + int testMutable() { return 42; } + + static MultipleAttrs *create() { + return new (malloc(sizeof(MultipleAttrs))) MultipleAttrs(); + } +}; + +struct __attribute__((swift_attr("import_as_ref"))) IntPair { + int a = 1; + int b = 2; + + int test() const { return b - a; } + int testMutable() { return b - a; } + + static IntPair *create() { return new (malloc(sizeof(IntPair))) IntPair(); } +}; + +void mutateIt(IntPair &x) { + x.a = 2; + x.b = 4; +} +IntPair passThroughByValue(IntPair x) { return x; } + +struct __attribute__((swift_attr("import_as_ref"))) RefHoldingPair { + // REVIEW-NOTE: I added support for this but then removed it, as this sort of + // indicates incorrect usage of a "reference type" and has weird semantics. + IntPair pair; + int otherValue = 3; + + int test() const { return otherValue - pair.test(); } + int testMutable() { return otherValue - pair.test(); } + + static RefHoldingPair *create() { + return new (malloc(sizeof(RefHoldingPair))) RefHoldingPair(); + } +}; + +struct __attribute__((swift_attr("import_as_ref"))) RefHoldingPairRef { + IntPair &pair; + int otherValue; + RefHoldingPairRef(IntPair &pair) : pair(pair), otherValue(42) {} + + int test() const { return otherValue - pair.test(); } + int testMutable() { return otherValue - pair.test(); } + + static RefHoldingPairRef *create() { + IntPair *pair = new (malloc(sizeof(IntPair))) IntPair(); + return new (malloc(sizeof(RefHoldingPairRef))) RefHoldingPairRef(*pair); + } +}; + +struct __attribute__((swift_attr("import_as_ref"))) RefHoldingPairPtr { + IntPair *pair; + int otherValue = 42; + + int test() const { return otherValue - pair->test(); } + int testMutable() { return otherValue - pair->test(); } + + static RefHoldingPairPtr *create() { + RefHoldingPairPtr *out = + new (malloc(sizeof(RefHoldingPairPtr))) RefHoldingPairPtr(); + out->pair = new (malloc(sizeof(IntPair))) IntPair(); + return out; + } +}; + +struct ValueHoldingPair { + IntPair pair; + int otherValue = 3; + + int test() const { return otherValue - pair.test(); } + int testMutable() { return otherValue - pair.test(); } + + static ValueHoldingPair *create() { + return new (malloc(sizeof(ValueHoldingPair))) ValueHoldingPair(); + } +}; + +struct __attribute__((swift_attr("import_as_ref"))) BigType { + int a = 1; + int b = 2; + char buffer[32]; + + int test() const { return b - a; } + int testMutable() { return b - a; } + + static BigType *create() { return new (malloc(sizeof(BigType))) BigType(); } +}; + +void mutateIt(BigType &x) { + x.a = 2; + x.b = 4; +} +BigType passThroughByValue(BigType x) { return x; } + +SWIFT_END_NULLABILITY_ANNOTATIONS + +#endif // TEST_INTEROP_CXX_FOREIGN_REFERENCE_INPUTS_POD_H diff --git a/test/Interop/Cxx/foreign-reference/Inputs/singleton.h b/test/Interop/Cxx/foreign-reference/Inputs/singleton.h new file mode 100644 index 0000000000000..b87267a6595f5 --- /dev/null +++ b/test/Interop/Cxx/foreign-reference/Inputs/singleton.h @@ -0,0 +1,90 @@ +#ifndef TEST_INTEROP_CXX_FOREIGN_REFERENCE_INPUTS_SINGLETON_H +#define TEST_INTEROP_CXX_FOREIGN_REFERENCE_INPUTS_SINGLETON_H + +#include + +#include "visibility.h" + +inline void *operator new(size_t, void *p) { return p; } + +SWIFT_BEGIN_NULLABILITY_ANNOTATIONS + +struct __attribute__((swift_attr("import_as_ref"))) DeletedDtor { + int value = 42; + + DeletedDtor() = default; + DeletedDtor(const DeletedDtor &) = default; + DeletedDtor(DeletedDtor &&) = default; + ~DeletedDtor() = delete; + + int test() const { return value; } + int testMutable() { return value; } + + static DeletedDtor *create() { + return new (malloc(sizeof(DeletedDtor))) DeletedDtor(); + } +}; + +void mutateIt(DeletedDtor &x) { x.value = 32; } + +struct __attribute__((swift_attr("import_as_ref"))) PrivateDtor { + int value = 42; + + PrivateDtor() = default; + PrivateDtor(const PrivateDtor &) = default; + PrivateDtor(PrivateDtor &&) = default; + + int test() const { return value; } + int testMutable() { return value; } + + static PrivateDtor *create() { + return new (malloc(sizeof(PrivateDtor))) PrivateDtor(); + } + +private: + ~PrivateDtor() {} +}; + +void mutateIt(PrivateDtor &x) { x.value = 32; } + +struct __attribute__((swift_attr("import_as_ref"))) DeletedSpecialMembers { + int value = 42; + + DeletedSpecialMembers() = default; + DeletedSpecialMembers(const DeletedSpecialMembers &) = delete; + DeletedSpecialMembers(DeletedSpecialMembers &&) = delete; + ~DeletedSpecialMembers() = delete; + + int test() const { return value; } + int testMutable() { return value; } + + static DeletedSpecialMembers *create() { + return new (malloc(sizeof(DeletedSpecialMembers))) DeletedSpecialMembers(); + } +}; + +void mutateIt(DeletedSpecialMembers &x) { x.value = 32; } + +struct __attribute__((swift_attr("import_as_ref"))) PrivateSpecialMembers { + int value = 42; + + PrivateSpecialMembers() = default; + + int test() const { return value; } + int testMutable() { return value; } + + static PrivateSpecialMembers *create() { + return new (malloc(sizeof(PrivateSpecialMembers))) PrivateSpecialMembers(); + } + +private: + PrivateSpecialMembers(const PrivateSpecialMembers &) = default; + PrivateSpecialMembers(PrivateSpecialMembers &&) = default; + ~PrivateSpecialMembers() = default; +}; + +void mutateIt(PrivateSpecialMembers &x) { x.value = 32; } + +SWIFT_END_NULLABILITY_ANNOTATIONS + +#endif // TEST_INTEROP_CXX_FOREIGN_REFERENCE_INPUTS_SINGLETON_H diff --git a/test/Interop/Cxx/foreign-reference/Inputs/visibility.h b/test/Interop/Cxx/foreign-reference/Inputs/visibility.h new file mode 100644 index 0000000000000..5eeb5b032bcee --- /dev/null +++ b/test/Interop/Cxx/foreign-reference/Inputs/visibility.h @@ -0,0 +1,25 @@ +#ifndef TEST_INTEROP_CXX_FOREIGN_REFERENCE_INPUTS_VISIBILITY_H +#define TEST_INTEROP_CXX_FOREIGN_REFERENCE_INPUTS_VISIBILITY_H + +#if __has_feature(nullability) +// Provide macros to temporarily suppress warning about the use of +// _Nullable and _Nonnull. +# define SWIFT_BEGIN_NULLABILITY_ANNOTATIONS \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Wnullability-extension\"") \ + _Pragma("clang assume_nonnull begin") +# define SWIFT_END_NULLABILITY_ANNOTATIONS \ + _Pragma("clang diagnostic pop") \ + _Pragma("clang assume_nonnull end") + +#else +// #define _Nullable and _Nonnull to nothing if we're not being built +// with a compiler that supports them. +# define _Nullable +# define _Nonnull +# define _Null_unspecified +# define SWIFT_BEGIN_NULLABILITY_ANNOTATIONS +# define SWIFT_END_NULLABILITY_ANNOTATIONS +#endif + +#endif // TEST_INTEROP_CXX_FOREIGN_REFERENCE_INPUTS_VISIBILITY_H diff --git a/test/Interop/Cxx/foreign-reference/Inputs/witness-table.h b/test/Interop/Cxx/foreign-reference/Inputs/witness-table.h new file mode 100644 index 0000000000000..64068709d82a0 --- /dev/null +++ b/test/Interop/Cxx/foreign-reference/Inputs/witness-table.h @@ -0,0 +1,53 @@ +#ifndef TEST_INTEROP_CXX_FOREIGN_REFERENCE_INPUTS_WITNESS_TABLE_H +#define TEST_INTEROP_CXX_FOREIGN_REFERENCE_INPUTS_WITNESS_TABLE_H + +#include + +inline void *operator new(size_t, void *p) { return p; } + +struct __attribute__((swift_attr("import_as_ref"))) CxxLinkedList { + int value = 3; + + CxxLinkedList * _Nullable next() { + if (value == 3) + return nullptr; + + return this + 1; + } +}; + +CxxLinkedList * _Nonnull makeLinkedList() { + CxxLinkedList *buff = (CxxLinkedList *)malloc(sizeof(CxxLinkedList) * 4); + buff[0].value = 0; + buff[1].value = 1; + buff[2].value = 2; + buff[3].value = 3; + return buff; +} + +struct __attribute__((swift_attr("import_as_ref"))) CxxSequence { + CxxLinkedList * _Nullable list = nullptr; + + CxxLinkedList * _Nullable next() { + if (list->value == 3) + return nullptr; + + auto * _Nullable tmp = list; + list = tmp + 1; + return tmp; + } +}; + +CxxSequence * _Nonnull makeSequence() { + CxxLinkedList *buff = (CxxLinkedList *)malloc(sizeof(CxxLinkedList) * 4); + buff[0].value = 0; + buff[1].value = 1; + buff[2].value = 2; + buff[3].value = 3; + + CxxSequence *seq = (CxxSequence *)malloc(sizeof(CxxSequence)); + seq->list = buff; + return seq; +} + +#endif // TEST_INTEROP_CXX_FOREIGN_REFERENCE_INPUTS_WITNESS_TABLE_H diff --git a/test/Interop/Cxx/foreign-reference/error-as-class-member.swift b/test/Interop/Cxx/foreign-reference/error-as-class-member.swift new file mode 100644 index 0000000000000..868ed325e1275 --- /dev/null +++ b/test/Interop/Cxx/foreign-reference/error-as-class-member.swift @@ -0,0 +1,27 @@ +// RUN: rm -rf %t +// RUN: split-file %s %t +// RUN: not %target-swift-frontend -emit-ir -I %t/Inputs %t/test.swift -enable-cxx-interop 2>&1 | %FileCheck %s + +// XFAIL: OS=windows-msvc + +//--- Inputs/module.modulemap +module Test { + header "test.h" + requires cplusplus +} + +//--- Inputs/test.h +#include + +inline void* operator new(size_t, void* p) { return p; } + +struct __attribute__((swift_attr("import_as_ref"))) Empty { + static Empty *create() { return new (malloc(sizeof(Empty))) Empty(); } +}; + +//--- test.swift + +import Test + +// CHECK: error: 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. +class C { let m: Empty = Empty.create() } diff --git a/test/Interop/Cxx/foreign-reference/error-as-struct-member.swift b/test/Interop/Cxx/foreign-reference/error-as-struct-member.swift new file mode 100644 index 0000000000000..517f05c27f822 --- /dev/null +++ b/test/Interop/Cxx/foreign-reference/error-as-struct-member.swift @@ -0,0 +1,25 @@ +// RUN: rm -rf %t +// RUN: split-file %s %t +// RUN: not %target-swift-frontend -emit-ir -I %t/Inputs %t/test.swift -enable-cxx-interop 2>&1 | %FileCheck %s + +//--- Inputs/module.modulemap +module Test { + header "test.h" + requires cplusplus +} + +//--- Inputs/test.h +#include + +inline void* operator new(size_t, void* p) { return p; } + +struct __attribute__((swift_attr("import_as_ref"))) Empty { + static Empty *create() { return new (malloc(sizeof(Empty))) Empty(); } +}; + +//--- test.swift + +import Test + +// CHECK: error: 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. +struct S { let m: Empty } diff --git a/test/Interop/Cxx/foreign-reference/error-in-generic-context.swift b/test/Interop/Cxx/foreign-reference/error-in-generic-context.swift new file mode 100644 index 0000000000000..1d59603b6e0cd --- /dev/null +++ b/test/Interop/Cxx/foreign-reference/error-in-generic-context.swift @@ -0,0 +1,27 @@ +// RUN: rm -rf %t +// RUN: split-file %s %t +// RUN: not %target-swift-frontend -emit-ir -I %t/Inputs %t/test.swift -enable-cxx-interop 2>&1 | %FileCheck %s + +//--- Inputs/module.modulemap +module Test { + header "test.h" + requires cplusplus +} + +//--- Inputs/test.h +#include + +inline void* operator new(size_t, void* p) { return p; } + +struct __attribute__((swift_attr("import_as_ref"))) Empty { + static Empty *create() { return new (malloc(sizeof(Empty))) Empty(); } +}; + +//--- test.swift + +import Test + +public func test(_ _: T) {} + +// CHECK: error: 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. +test(Empty.create()) diff --git a/test/Interop/Cxx/foreign-reference/move-only-irgen.swift b/test/Interop/Cxx/foreign-reference/move-only-irgen.swift new file mode 100644 index 0000000000000..abf7be9c0a9bb --- /dev/null +++ b/test/Interop/Cxx/foreign-reference/move-only-irgen.swift @@ -0,0 +1,29 @@ +// RUN: %target-swift-emit-ir %s -I %S/Inputs -enable-cxx-interop -validate-tbd-against-ir=none -disable-llvm-verify | %FileCheck %s + +import MoveOnly + +// TODO: this should not be opaque. +// CHECK: %TSo8MoveOnlyV = type opaque +// CHECK: %struct.MoveOnly = type { i8 } + + +// CHECK-LABEL: define {{.*}}swiftcc void @"$s4main4testyyF" + +// CHECK: [[X:%.*]] = alloca %TSo8MoveOnlyV* +// CHECK: [[TMP:%.*]] = alloca %TSo8MoveOnlyV* + +// CHECK: [[CREATED:%.*]] = call %struct.MoveOnly* @{{_ZN8MoveOnly6createEv|"\?create\@MoveOnly\@\@SAPEAU1\@XZ"}}() +// CHECK: [[SWIFT_CREATED:%.*]] = bitcast %struct.MoveOnly* [[CREATED]] to %TSo8MoveOnlyV* +// CHECK: store %TSo8MoveOnlyV* [[SWIFT_CREATED]], %TSo8MoveOnlyV** [[X]] +// CHECK: store %TSo8MoveOnlyV* [[SWIFT_CREATED]], %TSo8MoveOnlyV** [[TMP]] + +// CHECK: [[TMP_LOAD:%.*]] = load %TSo8MoveOnlyV*, %TSo8MoveOnlyV** [[TMP]] +// CHECK: [[CLANG_CREATED:%.*]] = bitcast %TSo8MoveOnlyV* [[TMP_LOAD]] to %struct.MoveOnly* +// CHECK: call i32 @{{_ZNK8MoveOnly4testEv|"\?test\@MoveOnly\@\@QEBAHXZ"}}(%struct.MoveOnly* [[CLANG_CREATED]]) + +// CHECK: ret void + +public func test() { + var x = MoveOnly.create() + _ = x.test() +} diff --git a/test/Interop/Cxx/foreign-reference/move-only-module-interface.swift b/test/Interop/Cxx/foreign-reference/move-only-module-interface.swift new file mode 100644 index 0000000000000..60e51d4bda9fa --- /dev/null +++ b/test/Interop/Cxx/foreign-reference/move-only-module-interface.swift @@ -0,0 +1,32 @@ +// RUN: %target-swift-ide-test -print-module -module-to-print=MoveOnly -I %S/Inputs -source-filename=x -enable-cxx-interop | %FileCheck %s + +// CHECK: class MoveOnly { +// CHECK-NOT: init +// CHECK: func test() -> Int32 +// CHECK: func testMutable() -> Int32 +// CHECK: class func create() -> MoveOnly +// CHECK: } +// CHECK-NOT: func moveIntoResult(_ x: MoveOnly) -> MoveOnly + +// CHECK: class HasMoveOnlyChild { +// CHECK-NOT: init +// CHECK-NOT: var child: MoveOnly +// CHECK: class func create() -> HasMoveOnlyChild +// CHECK: } +// CHECK-NOT: func moveIntoResult(_ x: HasMoveOnlyChild) -> HasMoveOnlyChild + +// CHECK: class PrivateCopyCtor { +// CHECK-NOT: init +// CHECK: func test() -> Int32 +// CHECK: func testMutable() -> Int32 +// CHECK: class func create() -> PrivateCopyCtor +// CHECK: } +// CHECK-NOT: func moveIntoResult(_ x: PrivateCopyCtor) -> PrivateCopyCtor + +// CHECK: class BadCopyCtor { +// CHECK-NOT: init +// CHECK: func test() -> Int32 +// CHECK: func testMutable() -> Int32 +// CHECK: class func create() -> BadCopyCtor +// CHECK: } +// CHECK-NOT: func moveIntoResult(_ x: BadCopyCtor) -> BadCopyCtor diff --git a/test/Interop/Cxx/foreign-reference/move-only-silgen.swift b/test/Interop/Cxx/foreign-reference/move-only-silgen.swift new file mode 100644 index 0000000000000..e56ce3098a0a0 --- /dev/null +++ b/test/Interop/Cxx/foreign-reference/move-only-silgen.swift @@ -0,0 +1,33 @@ +// RUN: %target-swift-emit-silgen %s -I %S/Inputs -enable-cxx-interop | %FileCheck %s + +import MoveOnly + +// CHECK-NOT: borrow +// CHECK-NOT: retain +// CHECK-NOT: release +// CHECK-LABEL: sil [ossa] @$s4main4testyyF : $@convention(thin) () -> () + +// CHECK: [[BOX:%.*]] = project_box {{.*}} : ${ var MoveOnly }, 0 + +// CHECK: [[CREATE_FN:%.*]] = function_ref @{{_ZN8MoveOnly6createEv|\?create\@MoveOnly\@\@SAPEAU1\@XZ}} : $@convention(c) () -> MoveOnly +// CHECK: [[CREATED_PTR:%.*]] = apply [[CREATE_FN]]() : $@convention(c) () -> MoveOnly +// CHECK: store [[CREATED_PTR]] to [trivial] [[BOX]] : $*MoveOnly +// CHECK: [[ACCESS_1:%.*]] = begin_access [read] [unknown] [[BOX]] : $*MoveOnly +// CHECK: [[X_1:%.*]] = load [trivial] [[ACCESS_1]] : $*MoveOnly + +// CHECK: [[TMP:%.*]] = alloc_stack $MoveOnly +// CHECK: store [[X_1]] to [trivial] [[TMP]] + +// CHECK: [[TEST_FN:%.*]] = function_ref @{{_ZNK8MoveOnly4testEv|\?test\@MoveOnly\@\@QEBAHXZ}} : $@convention(c) (@in_guaranteed MoveOnly) -> Int32 +// CHECK: apply [[TEST_FN]]([[TMP]]) : $@convention(c) (@in_guaranteed MoveOnly) -> Int32 + +// CHECK: return +// CHECK-LABEL: end sil function '$s4main4testyyF' +public func test() { + var x = MoveOnly.create() + _ = x.test() +} + +// CHECK-LABEL: sil [clang MoveOnly.create] @{{_ZN8MoveOnly6createEv|\?create\@MoveOnly\@\@SAPEAU1\@XZ}} : $@convention(c) () -> MoveOnly + +// CHECK-LABEL: sil [clang MoveOnly.test] @{{_ZNK8MoveOnly4testEv|\?test\@MoveOnly\@\@QEBAHXZ}} : $@convention(c) (@in_guaranteed MoveOnly) -> Int32 diff --git a/test/Interop/Cxx/foreign-reference/move-only.swift b/test/Interop/Cxx/foreign-reference/move-only.swift new file mode 100644 index 0000000000000..1dfa4271a9de1 --- /dev/null +++ b/test/Interop/Cxx/foreign-reference/move-only.swift @@ -0,0 +1,43 @@ +// RUN: %target-run-simple-swift(-I %S/Inputs/ -Xfrontend -enable-cxx-interop -Xfrontend -validate-tbd-against-ir=none -Xfrontend -disable-llvm-verify) +// +// REQUIRES: executable_test + +import StdlibUnittest +import MoveOnly + +@inline(never) +func blackHole(_ t: (BadCopyCtor, BadCopyCtor)) { } + +var MoveOnlyTestSuite = TestSuite("Move only types that are marked as foreign references") + +MoveOnlyTestSuite.test("MoveOnly") { + var x = MoveOnly.create() + expectEqual(x.test(), 42) + expectEqual(x.testMutable(), 42) + + x = MoveOnly.create() + expectEqual(x.test(), 42) +} + +MoveOnlyTestSuite.test("PrivateCopyCtor") { + var x = PrivateCopyCtor.create() + expectEqual(x.test(), 42) + expectEqual(x.testMutable(), 42) + + x = PrivateCopyCtor.create() + expectEqual(x.test(), 42) +} + +MoveOnlyTestSuite.test("BadCopyCtor") { + var x = BadCopyCtor.create() + expectEqual(x.test(), 42) + expectEqual(x.testMutable(), 42) + + x = BadCopyCtor.create() + expectEqual(x.test(), 42) + + let t = (x, x) // Copy this around just to make sure we don't call the copy ctor. + blackHole(t) +} + +runAllTests() diff --git a/test/Interop/Cxx/foreign-reference/not-any-object.swift b/test/Interop/Cxx/foreign-reference/not-any-object.swift new file mode 100644 index 0000000000000..07ba20d83a34f --- /dev/null +++ b/test/Interop/Cxx/foreign-reference/not-any-object.swift @@ -0,0 +1,29 @@ +// RUN: rm -rf %t +// RUN: split-file %s %t +// RUN: %target-swift-frontend -typecheck -verify -verify-ignore-unknown -I %t/Inputs %t/test.swift -enable-cxx-interop + +// REQUIRES: objc_interop + +//--- Inputs/module.modulemap +module Test { + header "test.h" + requires cplusplus +} + +//--- Inputs/test.h +#include + +inline void* operator new(unsigned long, void* p) { return p; } + +struct __attribute__((swift_attr("import_as_ref"))) Empty { + static Empty *create() { return new (malloc(sizeof(Empty))) Empty(); } +}; + +//--- test.swift + +import Test; + +public func test(_ _: AnyObject) {} + +// TODO: make this a better error. +test(Empty.create()) // expected-error {{type of expression is ambiguous without more context}} diff --git a/test/Interop/Cxx/foreign-reference/nullable-module-interface.swift b/test/Interop/Cxx/foreign-reference/nullable-module-interface.swift new file mode 100644 index 0000000000000..b63580bf60754 --- /dev/null +++ b/test/Interop/Cxx/foreign-reference/nullable-module-interface.swift @@ -0,0 +1,15 @@ +// RUN: %target-swift-ide-test -print-module -module-to-print=Nullable -I %S/Inputs -source-filename=x -enable-cxx-interop | %FileCheck %s + +// CHECK: class Empty { +// CHECK: func test() -> Int32 +// CHECK: class func create() -> Empty! +// CHECK: } +// CHECK: func mutateIt(_: Empty) + +// CHECK: class IntPair { +// CHECK: var a: Int32 +// CHECK: var b: Int32 +// CHECK: func test() -> Int32 +// CHECK: class func create() -> IntPair! +// CHECK: } +// CHECK: func mutateIt(_ x: IntPair!) diff --git a/test/Interop/Cxx/foreign-reference/nullable.swift b/test/Interop/Cxx/foreign-reference/nullable.swift new file mode 100644 index 0000000000000..a92380f9dcd5b --- /dev/null +++ b/test/Interop/Cxx/foreign-reference/nullable.swift @@ -0,0 +1,34 @@ +// RUN: %target-run-simple-swift(-I %S/Inputs/ -Xfrontend -enable-cxx-interop -Xfrontend -validate-tbd-against-ir=none -Xfrontend -disable-llvm-verify -g) +// +// REQUIRES: executable_test + +import StdlibUnittest +import Nullable + +var NullableTestSuite = TestSuite("Foreign references that are nullable") + +NullableTestSuite.test("Empty") { + var x = Empty.create() + expectEqual(x!.test(), 42) + + mutateIt(x!) + + x = Empty.create() ?? Empty.create()! + expectEqual(x!.test(), 42) +} + +NullableTestSuite.test("IntPair") { + var x = IntPair.create() + expectEqual(x!.test(), 1) + + mutateIt(x) + expectEqual(x!.test(), 2) + + x!.b = 42 + expectEqual(x!.test(), 40) + + x = IntPair.create() ?? IntPair.create()! + expectEqual(x!.test(), 1) +} + +runAllTests() diff --git a/test/Interop/Cxx/foreign-reference/pod-irgen.swift b/test/Interop/Cxx/foreign-reference/pod-irgen.swift new file mode 100644 index 0000000000000..4e058aa3787f2 --- /dev/null +++ b/test/Interop/Cxx/foreign-reference/pod-irgen.swift @@ -0,0 +1,33 @@ +// RUN: %target-swift-emit-ir %s -I %S/Inputs -enable-cxx-interop -validate-tbd-against-ir=none -disable-llvm-verify | %FileCheck %s + +import POD + +// TODO: this should not be opaque. +// CHECK: %TSo7IntPairV = type <{ %Ts5Int32V, %Ts5Int32V }> +// CHECK: %struct.IntPair = type { i32, i32 } + +// CHECK-LABEL: define {{.*}}swiftcc void @"$s4main4testyyF" + +// CHECK: [[X:%.*]] = alloca %TSo7IntPairV* +// CHECK: [[TMP:%.*]] = alloca %TSo7IntPairV* + +// CHECK: [[CREATED:%.*]] = call %struct.IntPair* @{{_ZN7IntPair6createEv|"\?create\@IntPair\@\@SAPEAU1\@XZ"}}() +// CHECK: [[SWIFT_CREATED:%.*]] = bitcast %struct.IntPair* [[CREATED]] to %TSo7IntPairV* +// CHECK: store %TSo7IntPairV* [[SWIFT_CREATED]], %TSo7IntPairV** [[X]] + +// CHECK: [[B_FIELD:%.*]] = getelementptr inbounds %TSo7IntPairV, %TSo7IntPairV* [[SWIFT_CREATED]], i32 0, i32 1 +// CHECK: [[INT_VALUE:%.*]] = getelementptr inbounds %Ts5Int32V, %Ts5Int32V* [[B_FIELD]], i32 0, i32 0 +// CHECK: store i32 42, i32* [[INT_VALUE]], align 4 + +// CHECK: store %TSo7IntPairV* [[SWIFT_CREATED]], %TSo7IntPairV** [[TMP]] +// CHECK: [[TMP_LOAD:%.*]] = load %TSo7IntPairV*, %TSo7IntPairV** [[TMP]] +// CHECK: [[CLANG_CREATED:%.*]] = bitcast %TSo7IntPairV* [[TMP_LOAD]] to %struct.IntPair* +// CHECK: call i32 @{{_ZNK7IntPair4testEv|"\?test\@IntPair\@\@QEBAHXZ"}}(%struct.IntPair* [[CLANG_CREATED]]) + +// CHECK: ret void + +public func test() { + var x = IntPair.create() + x.b = 42 + _ = x.test() +} diff --git a/test/Interop/Cxx/foreign-reference/pod-module-interface.swift b/test/Interop/Cxx/foreign-reference/pod-module-interface.swift new file mode 100644 index 0000000000000..f1b268a584d3f --- /dev/null +++ b/test/Interop/Cxx/foreign-reference/pod-module-interface.swift @@ -0,0 +1,77 @@ +// RUN: %target-swift-ide-test -print-module -module-to-print=POD -I %S/Inputs -source-filename=x -enable-cxx-interop | %FileCheck %s + +// CHECK: class Empty { +// CHECK-NOT: init +// CHECK: func test() -> Int32 +// CHECK: func testMutable() -> Int32 +// CHECK: class func create() -> Empty +// CHECK: } +// CHECK: func mutateIt(_: Empty) +// CHECK-NOT: func passThroughByValue(_ x: Empty) -> Empty + +// CHECK: class MultipleAttrs { +// CHECK-NOT: init +// CHECK: func test() -> Int32 +// CHECK: func testMutable() -> Int32 +// CHECK: class func create() -> MultipleAttrs +// CHECK: } + +// CHECK: class IntPair { +// CHECK-NOT: init +// CHECK: var a: Int32 +// CHECK: var b: Int32 +// CHECK: func test() -> Int32 +// CHECK: func testMutable() -> Int32 +// CHECK: class func create() -> IntPair +// CHECK: } +// CHECK: func mutateIt(_ x: IntPair) +// CHECK-NOT: func passThroughByValue(_ x: IntPair) -> IntPair + +// CHECK: class RefHoldingPair { +// CHECK-NOT: init +// CHECK-NOT: pair +// CHECK: var otherValue: Int32 +// CHECK: func test() -> Int32 +// CHECK: func testMutable() -> Int32 +// CHECK: class func create() -> RefHoldingPair +// CHECK: } + +// CHECK: class RefHoldingPairRef { +// CHECK-NOT: init +// CHECK: var pair: IntPair +// CHECK: var otherValue: Int32 +// CHECK: func test() -> Int32 +// CHECK: func testMutable() -> Int32 +// CHECK: class func create() -> RefHoldingPairRef +// CHECK: } + +// CHECK: class RefHoldingPairPtr { +// CHECK-NOT: init +// CHECK: var pair: IntPair +// CHECK: var otherValue: Int32 +// CHECK: func test() -> Int32 +// CHECK: func testMutable() -> Int32 +// CHECK: class func create() -> RefHoldingPairPtr +// CHECK: } + +// CHECK: struct ValueHoldingPair { +// CHECK-NOT: init +// CHECK-NOT: pair +// CHECK: init() +// CHECK: var otherValue: Int32 +// CHECK: func test() -> Int32 +// CHECK: mutating func testMutable() -> Int32 +// CHECK: static func create() -> UnsafeMutablePointer +// CHECK: } + +// CHECK: class BigType { +// CHECK-NOT: init +// CHECK: var a: Int32 +// CHECK: var b: Int32 +// CHECK: var buffer: +// CHECK: func test() -> Int32 +// CHECK: func testMutable() -> Int32 +// CHECK: class func create() -> BigType +// CHECK: } +// CHECK: func mutateIt(_ x: BigType) +// CHECK-NOT: func passThroughByValue(_ x: BigType) -> BigType diff --git a/test/Interop/Cxx/foreign-reference/pod-silgen.swift b/test/Interop/Cxx/foreign-reference/pod-silgen.swift new file mode 100644 index 0000000000000..bf7192f2dc40b --- /dev/null +++ b/test/Interop/Cxx/foreign-reference/pod-silgen.swift @@ -0,0 +1,39 @@ +// RUN: %target-swift-emit-silgen %s -I %S/Inputs -enable-cxx-interop | %FileCheck %s + +import POD + +// CHECK-NOT: borrow +// CHECK-NOT: retain +// CHECK-NOT: release +// CHECK-LABEL: sil [ossa] @$s4main4testyyF : $@convention(thin) () -> () + +// CHECK: [[BOX:%.*]] = project_box {{.*}} : ${ var IntPair }, 0 + +// CHECK: [[CREATE_FN:%.*]] = function_ref @{{_ZN7IntPair6createEv|\?create\@IntPair\@\@SAPEAU1\@XZ}} : $@convention(c) () -> IntPair +// CHECK: [[CREATED_PTR:%.*]] = apply [[CREATE_FN]]() : $@convention(c) () -> IntPair +// CHECK: store [[CREATED_PTR]] to [trivial] [[BOX]] : $*IntPair +// CHECK: [[ACCESS_1:%.*]] = begin_access [read] [unknown] [[BOX]] : $*IntPair +// CHECK: [[X_1:%.*]] = load [trivial] [[ACCESS_1]] : $*IntPair + +// CHECK: [[X_B:%.*]] = ref_element_addr [[X_1]] : $IntPair, #IntPair.b +// CHECK: [[ACCESS_2:%.*]] = begin_access [modify] [dynamic] [[X_B]] : $*Int32 +// CHECK: assign {{.*}} to [[ACCESS_2]] : $*Int32 + +// CHECK: [[ACCESS_3:%.*]] = begin_access [read] [unknown] [[BOX]] : $*IntPair +// CHECK: [[X_2:%.*]] = load [trivial] [[ACCESS_3]] : $*IntPair +// CHECK: [[TMP:%.*]] = alloc_stack $IntPair +// CHECK: store [[X_2]] to [trivial] [[TMP]] +// CHECK: [[TEST_FN:%.*]] = function_ref @{{_ZNK7IntPair4testEv|\?test\@IntPair\@\@QEBAHXZ}} : $@convention(c) (@in_guaranteed IntPair) -> Int32 +// CHECK: apply [[TEST_FN]]([[TMP]]) : $@convention(c) (@in_guaranteed IntPair) -> Int32 + +// CHECK: return +// CHECK-LABEL: end sil function '$s4main4testyyF' +public func test() { + var x = IntPair.create() + x.b = 42 + _ = x.test() +} + +// CHECK-LABEL: sil [clang IntPair.create] @{{_ZN7IntPair6createEv|\?create\@IntPair\@\@SAPEAU1\@XZ}} : $@convention(c) () -> IntPair + +// CHECK-LABEL: sil [clang IntPair.test] @{{_ZNK7IntPair4testEv|\?test\@IntPair\@\@QEBAHXZ}} : $@convention(c) (@in_guaranteed IntPair) -> Int32 diff --git a/test/Interop/Cxx/foreign-reference/pod.swift b/test/Interop/Cxx/foreign-reference/pod.swift new file mode 100644 index 0000000000000..ea90bd34bb0c1 --- /dev/null +++ b/test/Interop/Cxx/foreign-reference/pod.swift @@ -0,0 +1,154 @@ +// RUN: %target-run-simple-swift(-I %S/Inputs/ -Xfrontend -enable-cxx-interop -Xfrontend -validate-tbd-against-ir=none -Xfrontend -disable-llvm-verify -g) +// +// REQUIRES: executable_test + +import StdlibUnittest +import POD + +// TODO: Waiting on foreign reference type metadata implementation. +// +// struct StructHoldingPair { +// var pair: IntPair +// }; +// +// class ClassHoldingPair { +// var pair: IntPair +// +// init(pair: IntPair) { self.pair = pair } +// }; + +var globalPair: IntPair? = nil + +var PODTestSuite = TestSuite("Plain old data types that are marked as foreign references") + +PODTestSuite.test("Empty") { + var x = Empty.create() + expectEqual(x.test(), 42) + expectEqual(x.testMutable(), 42) + + mutateIt(x) + + x = Empty.create() + expectEqual(x.test(), 42) +} + +PODTestSuite.test("var IntPair") { + var x = IntPair.create() + expectEqual(x.test(), 1) + expectEqual(x.testMutable(), 1) + + mutateIt(x) + expectEqual(x.test(), 2) + expectEqual(x.testMutable(), 2) + + x.b = 42 + expectEqual(x.test(), 40) + expectEqual(x.testMutable(), 40) + + x = IntPair.create() + expectEqual(x.test(), 1) +} + +PODTestSuite.test("let IntPair") { + let x = IntPair.create() + expectEqual(x.test(), 1) + expectEqual(x.testMutable(), 1) + + mutateIt(x) + expectEqual(x.test(), 2) + expectEqual(x.testMutable(), 2) + + x.b = 42 + expectEqual(x.test(), 40) + expectEqual(x.testMutable(), 40) +} + +PODTestSuite.test("global") { + globalPair = IntPair.create() + expectEqual(globalPair!.test(), 1) + expectEqual(globalPair!.testMutable(), 1) + + mutateIt(globalPair!) + expectEqual(globalPair!.test(), 2) + expectEqual(globalPair!.testMutable(), 2) + + globalPair!.b = 42 + expectEqual(globalPair!.test(), 40) + expectEqual(globalPair!.testMutable(), 40) + + globalPair = IntPair.create() + expectEqual(globalPair!.test(), 1) +} + +PODTestSuite.test("RefHoldingPairRef") { + var x = RefHoldingPairRef.create() + expectEqual(x.test(), 41) + expectEqual(x.testMutable(), 41) + + x.pair.b = 42 + expectEqual(x.test(), 1) + expectEqual(x.testMutable(), 1) + + x = RefHoldingPairRef.create() + expectEqual(x.test(), 41) +} + +PODTestSuite.test("RefHoldingPairPtr") { + var x = RefHoldingPairPtr.create() + expectEqual(x.test(), 41) + expectEqual(x.testMutable(), 41) + + x.pair.b = 42 + expectEqual(x.test(), 1) + expectEqual(x.testMutable(), 1) + + x = RefHoldingPairPtr.create() + expectEqual(x.test(), 41) +} + +// TODO: Waiting on foreign reference types metadata implementation. +// +// PODTestSuite.test("StructHoldingPair") { +// var x = StructHoldingPair(pair: IntPair.create()) +// expectEqual(x.pair.test(), 1) +// expectEqual(x.pair.testMutable(), 1) +// +// mutateIt(x.pair) +// expectEqual(x.pair.test(), 2) +// expectEqual(x.pair.testMutable(), 2) +// +// x.pair = IntPair.create() +// expectEqual(x.pair.test(), 1) +// } +// +// PODTestSuite.test("ClassHoldingPair") { +// var x = ClassHoldingPair(pair: IntPair.create()) +// expectEqual(x.pair.test(), 1) +// expectEqual(x.pair.testMutable(), 1) +// +// mutateIt(x.pair) +// expectEqual(x.pair.test(), 2) +// expectEqual(x.pair.testMutable(), 2) +// +// x.pair = IntPair.create() +// expectEqual(x.pair.test(), 1) +// } + +PODTestSuite.test("BigType") { + var x = BigType.create() + expectEqual(x.test(), 1) + expectEqual(x.testMutable(), 1) + + mutateIt(x) + expectEqual(x.test(), 2) + expectEqual(x.testMutable(), 2) + + x.b = 42 + expectEqual(x.test(), 40) + expectEqual(x.testMutable(), 40) + + x = BigType.create() + expectEqual(x.test(), 1) +} + +runAllTests() diff --git a/test/Interop/Cxx/foreign-reference/singleton-irgen.swift b/test/Interop/Cxx/foreign-reference/singleton-irgen.swift new file mode 100644 index 0000000000000..159c7f13c1773 --- /dev/null +++ b/test/Interop/Cxx/foreign-reference/singleton-irgen.swift @@ -0,0 +1,32 @@ +// RUN: %target-swift-emit-ir %s -I %S/Inputs -enable-cxx-interop -validate-tbd-against-ir=none -disable-llvm-verify | %FileCheck %s + +import Singleton + +// TODO: this should not be opaque. +// CHECK: %TSo21DeletedSpecialMembersV = type opaque +// CHECK: %struct.DeletedSpecialMembers = type { i32 } + +// CHECK-LABEL: define {{.*}}swiftcc void @"$s4main4testyyF" + +// CHECK: [[X:%.*]] = alloca %TSo21DeletedSpecialMembersV* +// CHECK: [[TMP:%.*]] = alloca %TSo21DeletedSpecialMembersV* + +// CHECK: [[CREATED:%.*]] = call %struct.DeletedSpecialMembers* @{{_ZN21DeletedSpecialMembers6createEv|"\?create\@DeletedSpecialMembers\@\@SAPEAU1\@XZ"}}() +// CHECK: [[SWIFT_CREATED:%.*]] = bitcast %struct.DeletedSpecialMembers* [[CREATED]] to %TSo21DeletedSpecialMembersV* +// CHECK: store %TSo21DeletedSpecialMembersV* [[SWIFT_CREATED]], %TSo21DeletedSpecialMembersV** [[X]] +// CHECK: store %TSo21DeletedSpecialMembersV* [[SWIFT_CREATED]], %TSo21DeletedSpecialMembersV** [[TMP]] + +// CHECK: [[TMP_LOAD:%.*]] = load %TSo21DeletedSpecialMembersV*, %TSo21DeletedSpecialMembersV** [[TMP]] +// CHECK: [[CLANG_CREATED:%.*]] = bitcast %TSo21DeletedSpecialMembersV* [[TMP_LOAD]] to %struct.DeletedSpecialMembers* +// CHECK: call i32 @{{_ZNK21DeletedSpecialMembers4testEv|"\?test\@DeletedSpecialMembers\@\@QEBAHXZ"}}(%struct.DeletedSpecialMembers* [[CLANG_CREATED]]) + +// CHECK: [[FROM:%.*]] = bitcast %TSo21DeletedSpecialMembersV* [[SWIFT_CREATED]] to %struct.DeletedSpecialMembers* +// CHECK: call void @{{_Z8mutateItR21DeletedSpecialMembers|"\?mutateIt\@\@YAXAEAUDeletedSpecialMembers\@\@\@Z"}}(%struct.DeletedSpecialMembers* [[FROM]]) + +// CHECK: ret void + +public func test() { + var x = DeletedSpecialMembers.create() + _ = x.test() + mutateIt(x) +} diff --git a/test/Interop/Cxx/foreign-reference/singleton-module-interface.swift b/test/Interop/Cxx/foreign-reference/singleton-module-interface.swift new file mode 100644 index 0000000000000..2dda051cc3a33 --- /dev/null +++ b/test/Interop/Cxx/foreign-reference/singleton-module-interface.swift @@ -0,0 +1,37 @@ +// RUN: %target-swift-ide-test -print-module -module-to-print=Singleton -I %S/Inputs -source-filename=x -enable-cxx-interop | %FileCheck %s + +// CHECK: class DeletedDtor { +// CHECK-NOT: init +// CHECK: var value: Int32 +// CHECK: func test() -> Int32 +// CHECK: func testMutable() -> Int32 +// CHECK: class func create() -> DeletedDtor +// CHECK: } +// CHECK: func mutateIt(_ x: DeletedDtor) + +// CHECK: class PrivateDtor { +// CHECK-NOT: init +// CHECK: var value: Int32 +// CHECK: func test() -> Int32 +// CHECK: func testMutable() -> Int32 +// CHECK: class func create() -> PrivateDtor +// CHECK: } +// CHECK: func mutateIt(_ x: PrivateDtor) + +// CHECK: class DeletedSpecialMembers { +// CHECK-NOT: init +// CHECK: var value: Int32 +// CHECK: func test() -> Int32 +// CHECK: func testMutable() -> Int32 +// CHECK: class func create() -> DeletedSpecialMembers +// CHECK: } +// CHECK: func mutateIt(_ x: DeletedSpecialMembers) + +// CHECK: class PrivateSpecialMembers { +// CHECK-NOT: init +// CHECK: var value: Int32 +// CHECK: func test() -> Int32 +// CHECK: func testMutable() -> Int32 +// CHECK: class func create() -> PrivateSpecialMembers +// CHECK: } +// CHECK: func mutateIt(_ x: PrivateSpecialMembers) diff --git a/test/Interop/Cxx/foreign-reference/singleton-silgen.swift b/test/Interop/Cxx/foreign-reference/singleton-silgen.swift new file mode 100644 index 0000000000000..f9353a56f7fda --- /dev/null +++ b/test/Interop/Cxx/foreign-reference/singleton-silgen.swift @@ -0,0 +1,41 @@ +// RUN: %target-swift-emit-silgen %s -I %S/Inputs -enable-cxx-interop | %FileCheck %s + +import Singleton + +// CHECK-NOT: borrow +// CHECK-NOT: retain +// CHECK-NOT: release +// CHECK-LABEL: sil [ossa] @$s4main4testyyF : $@convention(thin) () -> () + +// CHECK: [[BOX:%.*]] = project_box {{.*}} : ${ var DeletedSpecialMembers }, 0 + +// CHECK: [[CREATE_FN:%.*]] = function_ref @{{_ZN21DeletedSpecialMembers6createEv|\?create\@DeletedSpecialMembers\@\@SAPEAU1\@XZ}} : $@convention(c) () -> DeletedSpecialMembers +// CHECK: [[CREATED_PTR:%.*]] = apply [[CREATE_FN]]() : $@convention(c) () -> DeletedSpecialMembers +// CHECK: store [[CREATED_PTR]] to [trivial] [[BOX]] : $*DeletedSpecialMembers +// CHECK: [[ACCESS_1:%.*]] = begin_access [read] [unknown] [[BOX]] : $*DeletedSpecialMembers +// CHECK: [[X_1:%.*]] = load [trivial] [[ACCESS_1]] : $*DeletedSpecialMembers + +// CHECK: [[TMP:%.*]] = alloc_stack $DeletedSpecialMembers +// CHECK: store [[X_1]] to [trivial] [[TMP]] + +// CHECK: [[TEST_FN:%.*]] = function_ref @{{_ZNK21DeletedSpecialMembers4testEv|\?test\@DeletedSpecialMembers\@\@QEBAHXZ}} : $@convention(c) (@in_guaranteed DeletedSpecialMembers) -> Int32 +// CHECK: apply [[TEST_FN]]([[TMP]]) : $@convention(c) (@in_guaranteed DeletedSpecialMembers) -> Int32 +// CHECK: [[ACCESS_2:%.*]] = begin_access [read] [unknown] [[BOX]] : $*DeletedSpecialMembers +// CHECK: [[X_2:%.*]] = load [trivial] [[ACCESS_2]] : $*DeletedSpecialMembers + +// CHECK: [[MOVE_IN_RES_FN:%.*]] = function_ref @{{_Z8mutateItR21DeletedSpecialMembers|\?mutateIt\@\@YAXAEAUDeletedSpecialMembers\@\@\@Z}} : $@convention(c) (DeletedSpecialMembers) -> () +// CHECK: apply [[MOVE_IN_RES_FN]]([[X_2]]) : $@convention(c) (DeletedSpecialMembers) -> () + +// CHECK: return +// CHECK-LABEL: end sil function '$s4main4testyyF' +public func test() { + var x = DeletedSpecialMembers.create() + _ = x.test() + mutateIt(x) +} + +// CHECK-LABEL: sil [clang DeletedSpecialMembers.create] @{{_ZN21DeletedSpecialMembers6createEv|\?create\@DeletedSpecialMembers\@\@SAPEAU1\@XZ}} : $@convention(c) () -> DeletedSpecialMembers + +// CHECK-LABEL: sil [clang DeletedSpecialMembers.test] @{{_ZNK21DeletedSpecialMembers4testEv|\?test\@DeletedSpecialMembers\@\@QEBAHXZ}} : $@convention(c) (@in_guaranteed DeletedSpecialMembers) -> Int32 + +// CHECK-LABEL: sil [serializable] [clang mutateIt] @{{_Z8mutateItR21DeletedSpecialMembers|\?mutateIt\@\@YAXAEAUDeletedSpecialMembers\@\@\@Z}} : $@convention(c) (DeletedSpecialMembers) -> () diff --git a/test/Interop/Cxx/foreign-reference/singleton.swift b/test/Interop/Cxx/foreign-reference/singleton.swift new file mode 100644 index 0000000000000..3902c73f47ee9 --- /dev/null +++ b/test/Interop/Cxx/foreign-reference/singleton.swift @@ -0,0 +1,74 @@ +// RUN: %target-run-simple-swift(-I %S/Inputs/ -Xfrontend -enable-cxx-interop -Xfrontend -validate-tbd-against-ir=none -Xfrontend -disable-llvm-verify) +// +// REQUIRES: executable_test + +import StdlibUnittest +import Singleton + +var SingletonTestSuite = TestSuite("Singleton types that are marked as foreign references") + +SingletonTestSuite.test("DeletedDtor") { + var x = DeletedDtor.create() + expectEqual(x.test(), 42) + expectEqual(x.testMutable(), 42) + + mutateIt(x) + expectEqual(x.test(), 32) + + x = DeletedDtor.create() + expectEqual(x.test(), 42) + + x.value = 22 + expectEqual(x.value, 22) + expectEqual(x.test(), 22) +} + +SingletonTestSuite.test("PrivateDtor") { + var x = PrivateDtor.create() + expectEqual(x.test(), 42) + expectEqual(x.testMutable(), 42) + + mutateIt(x) + expectEqual(x.test(), 32) + + x = PrivateDtor.create() + expectEqual(x.test(), 42) + + x.value = 22 + expectEqual(x.value, 22) + expectEqual(x.test(), 22) +} + +SingletonTestSuite.test("DeletedSpecialMembers") { + var x = DeletedSpecialMembers.create() + expectEqual(x.test(), 42) + expectEqual(x.testMutable(), 42) + + mutateIt(x) + expectEqual(x.test(), 32) + + x = DeletedSpecialMembers.create() + expectEqual(x.test(), 42) + + x.value = 22 + expectEqual(x.value, 22) + expectEqual(x.test(), 22) +} + +SingletonTestSuite.test("PrivateSpecialMembers") { + var x = PrivateSpecialMembers.create() + expectEqual(x.test(), 42) + expectEqual(x.testMutable(), 42) + + mutateIt(x) + expectEqual(x.test(), 32) + + x = PrivateSpecialMembers.create() + expectEqual(x.test(), 42) + + x.value = 22 + expectEqual(x.value, 22) + expectEqual(x.test(), 22) +} + +runAllTests() diff --git a/test/Interop/Cxx/foreign-reference/witness-table.swift b/test/Interop/Cxx/foreign-reference/witness-table.swift new file mode 100644 index 0000000000000..8caba272e0386 --- /dev/null +++ b/test/Interop/Cxx/foreign-reference/witness-table.swift @@ -0,0 +1,57 @@ +// RUN: %target-run-simple-swift(-I %S/Inputs/ -Xfrontend -enable-cxx-interop -Xfrontend -validate-tbd-against-ir=none -Xfrontend -disable-llvm-verify -g) +// +// REQUIRES: executable_test +// REQUIRES: foreign-reference-type-metadata-support + +import StdlibUnittest +import WitnessTable + +public protocol ListNode { + associatedtype Element + func next() -> Element? +} + +public struct List : Sequence, IteratorProtocol + where NodeType.Element == NodeType { + private var currentNode: NodeType? + + public init(startingAt start: NodeType?) { currentNode = start } + + public mutating func next() -> NodeType? { + if let node = currentNode { + currentNode = node.next() + return node + } + return nil + } +} + +extension CxxLinkedList : ListNode { } + +var WitnessTableTestSuite = TestSuite("Use foreign reference in a generic context") + +WitnessTableTestSuite.test("As generic argument to List") { + let first = makeLinkedList() + let list = List(startingAt: first) + var count = 0 + for e in list { + expectEqual(count, Int(e.value)) + count += 1 + } + expectEqual(count, 4) +} + +extension CxxSequence : Sequence, IteratorProtocol { } + +WitnessTableTestSuite.test("As a Sequence") { + let list = makeSequence() + var count = 0 + for e in list { + expectEqual(count, Int(e.value)) + count += 1 + } + expectEqual(count, 3) +} + +runAllTests() +