mirror of
https://github.com/apple/swift.git
synced 2026-06-20 15:42:51 +02:00
[cxx-interop] Import inheritance hierarchy of foreign reference types
This teaches ClangImporter to represent a base class of a C++ foreign reference type as a Swift superclass if the base class is an FRT itself, and is a primary base class. Such base types are stored at offset zero within the derived type, which makes casting trivial. This makes the following possible: * casting an instance of derived FRT to base FRT explicitly with `as` * passing an instance of derived FRT as a function parameter of type base FRT * calling a method of base FRT on an instance of derived FRT without the compiler having to clone methods Several constructs are not supported: * downcasting, i.e. casting an instance of base FRT to derived FRT * casting arrays/sets/dictionaries of FRTs * casting to virtual bases Support for casting to a base FRT that is not the primary base (not at offset zero) can be added in a follow-up change. rdar://85881664 / resolves https://github.com/swiftlang/swift/issues/80231
This commit is contained in:
@@ -6612,6 +6612,9 @@ ERROR(invalid_ownership_incompatible_class,none,
|
||||
ERROR(invalid_ownership_incompatible_foreign_reference_type,none,
|
||||
"%0 is incompatible with 'weak' references, because it is a foreign reference type",
|
||||
(Type))
|
||||
ERROR(downcast_to_foreign_reference_type,none,
|
||||
"downcast from %0 to %1 is not supported for foreign reference types",
|
||||
(Type, Type))
|
||||
ERROR(invalid_ownership_with_optional,none,
|
||||
"%0 variable cannot have optional type", (ReferenceOwnership))
|
||||
ERROR(invalid_ownership_not_optional,none,
|
||||
|
||||
@@ -366,9 +366,12 @@ class ForeignReferenceTypeInfo {
|
||||
|
||||
llvm::PointerIntPair<const clang::RecordDecl *, 2> BaseAndFlags;
|
||||
|
||||
ForeignReferenceTypeInfo(const clang::RecordDecl *decl, bool isValid,
|
||||
bool isRef)
|
||||
: BaseAndFlags{decl} {
|
||||
const clang::CXXRecordDecl *primarySuperclass = nullptr;
|
||||
|
||||
ForeignReferenceTypeInfo(const clang::RecordDecl *decl,
|
||||
const clang::CXXRecordDecl *primarySuperclass,
|
||||
bool isValid, bool isRef)
|
||||
: BaseAndFlags{decl}, primarySuperclass(primarySuperclass) {
|
||||
unsigned int flags = 0;
|
||||
flags |= isValid ? FlagIsValid : 0;
|
||||
flags |= isRef ? FlagIsRef : 0;
|
||||
@@ -381,14 +384,16 @@ public:
|
||||
|
||||
/// Not a reference type
|
||||
static ForeignReferenceTypeInfo Value(bool isValid = true) {
|
||||
return {nullptr, isValid, /*isRef=*/false};
|
||||
return {nullptr, nullptr, isValid, /*isRef=*/false};
|
||||
}
|
||||
|
||||
/// A shared reference type using the retain/release functions from \a decl.
|
||||
static ForeignReferenceTypeInfo Shared(const clang::RecordDecl *decl,
|
||||
bool isValid = true) {
|
||||
static ForeignReferenceTypeInfo
|
||||
Shared(const clang::RecordDecl *decl,
|
||||
const clang::CXXRecordDecl *primarySuperclass = nullptr,
|
||||
bool isValid = true) {
|
||||
ASSERT(decl && "shared reference must have a non-null base decl");
|
||||
return {decl, isValid, /*isRef=*/true};
|
||||
return {decl, primarySuperclass, isValid, /*isRef=*/true};
|
||||
}
|
||||
|
||||
/// The base decl that is annotated with the retain/release functions that
|
||||
@@ -413,6 +418,14 @@ public:
|
||||
///
|
||||
/// This is independent of whether those attributes are actually valid.
|
||||
bool isReference() const { return BaseAndFlags.getInt() & FlagIsRef; }
|
||||
|
||||
/// The single FRT base that is the primary (first) direct base of this
|
||||
/// type, suitable for use as the Swift superclass. Returns nullptr if there
|
||||
/// is no single primary FRT base (e.g., multiple FRT bases, or the FRT
|
||||
/// base is not the first direct base).
|
||||
const clang::CXXRecordDecl *getPrimarySuperclass() const {
|
||||
return primarySuperclass;
|
||||
}
|
||||
};
|
||||
|
||||
struct ForeignReferenceTypeInfoDescriptor {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "swift/ClangImporter/ClangImporterRequests.h"
|
||||
#include "clang/AST/DeclCXX.h"
|
||||
#include "clang/AST/DeclObjC.h"
|
||||
#include "clang/AST/RecordLayout.h"
|
||||
#include "clang/AST/Type.h"
|
||||
#include "llvm/ADT/SetVector.h"
|
||||
#include "llvm/ADT/SmallPtrSet.h"
|
||||
@@ -24,6 +25,80 @@ bool importer::hasImportAsOpaquePointerAttr(const clang::RecordDecl *decl) {
|
||||
});
|
||||
}
|
||||
|
||||
static ForeignReferenceTypeInfo
|
||||
checkForeignReferenceType(const clang::CXXRecordDecl *decl,
|
||||
ClangImporter::Implementation *Impl);
|
||||
|
||||
static std::pair<std::optional<StringRef>, std::optional<StringRef>>
|
||||
getRetainReleaseOperations(const clang::RecordDecl *decl) {
|
||||
if (!decl->hasAttrs())
|
||||
return {std::nullopt, std::nullopt};
|
||||
|
||||
std::optional<StringRef> retain, release;
|
||||
for (auto attr : decl->getAttrs()) {
|
||||
auto swiftAttr = dyn_cast<clang::SwiftAttrAttr>(attr);
|
||||
if (!swiftAttr)
|
||||
continue;
|
||||
auto attrStr = swiftAttr->getAttribute();
|
||||
|
||||
if (attrStr.consume_front("retain:"))
|
||||
retain = attrStr;
|
||||
else if (attrStr.consume_front("release:"))
|
||||
release = attrStr;
|
||||
}
|
||||
return {retain, release};
|
||||
}
|
||||
|
||||
/// If \p decl has exactly one direct FRT base and it is at offset 0 in the
|
||||
/// C++ layout, return it. Being at offset 0 means a pointer bitcast suffices
|
||||
/// for upcasting in IRGen. This is the case when the FRT base is the first
|
||||
/// base, or when all bases before it are empty (empty base optimization).
|
||||
static const clang::CXXRecordDecl *
|
||||
findPrimarySuperclassBase(const clang::CXXRecordDecl *decl,
|
||||
const clang::CXXRecordDecl *baseWithLifetimeOps) {
|
||||
if (!decl->hasDefinition())
|
||||
return nullptr;
|
||||
|
||||
const clang::CXXRecordDecl *singleFRTBase = nullptr;
|
||||
ForeignReferenceTypeInfo singleFRTBaseInfo;
|
||||
|
||||
for (auto base : decl->bases()) {
|
||||
if (auto baseRecordDecl = base.getType()->getAsCXXRecordDecl()) {
|
||||
auto baseFRTInfo =
|
||||
checkForeignReferenceType(baseRecordDecl, /*Impl=*/nullptr);
|
||||
if (!baseFRTInfo.isReference() || base.isVirtual() ||
|
||||
base.getAccessSpecifier() != clang::AccessSpecifier::AS_public)
|
||||
continue;
|
||||
|
||||
if (singleFRTBase)
|
||||
// Bail if we found multiple FRT bases.
|
||||
return nullptr;
|
||||
|
||||
singleFRTBase = baseRecordDecl;
|
||||
singleFRTBaseInfo = baseFRTInfo;
|
||||
}
|
||||
}
|
||||
|
||||
if (!singleFRTBase)
|
||||
return nullptr;
|
||||
|
||||
auto &clangCtx = decl->getASTContext();
|
||||
auto &layout = clangCtx.getASTRecordLayout(decl);
|
||||
if (!layout.getBaseClassOffset(singleFRTBase).isZero())
|
||||
return nullptr;
|
||||
|
||||
// If decl has attributes with lifetime operations, check that they match.
|
||||
auto [overriddenRetain, overriddenRelease] =
|
||||
getRetainReleaseOperations(baseWithLifetimeOps);
|
||||
auto [baseRetain, baseRelease] =
|
||||
getRetainReleaseOperations(singleFRTBaseInfo.getDecl());
|
||||
if ((overriddenRelease && overriddenRelease != baseRelease) ||
|
||||
(overriddenRetain && overriddenRetain != baseRetain))
|
||||
return nullptr;
|
||||
|
||||
return singleFRTBase;
|
||||
}
|
||||
|
||||
/// This routine determines whether \a decl is a foreign reference type, and
|
||||
/// whether its foreign reference type attributes are valid. This determinition
|
||||
/// considers attributes annotated on both \a decl itself as well as its
|
||||
@@ -39,7 +114,8 @@ checkForeignReferenceType(const clang::CXXRecordDecl *decl,
|
||||
ClangImporter::Implementation *Impl) {
|
||||
// This was explicitly annotated. Just trust the annotation.
|
||||
if (importer::hasImportReferenceAttr(decl))
|
||||
return ForeignReferenceTypeInfo::Shared(decl);
|
||||
return ForeignReferenceTypeInfo::Shared(decl,
|
||||
findPrimarySuperclassBase(decl, decl));
|
||||
|
||||
if (!decl->hasDefinition())
|
||||
// If this doesn't have a definition, there's no inheritance info to check.
|
||||
@@ -165,7 +241,8 @@ checkForeignReferenceType(const clang::CXXRecordDecl *decl,
|
||||
return ForeignReferenceTypeInfo::Value(/*isValid=*/false);
|
||||
}
|
||||
|
||||
return ForeignReferenceTypeInfo::Shared(baseFRT);
|
||||
return ForeignReferenceTypeInfo::Shared(
|
||||
baseFRT, findPrimarySuperclassBase(decl, baseFRT));
|
||||
}
|
||||
|
||||
void swift::simple_display(llvm::raw_ostream &out,
|
||||
|
||||
@@ -8093,7 +8093,7 @@ ClangImporter::createEmbeddedBridgingHeaderCacheKey(
|
||||
"ChainedHeaderIncludeTree -> EmbeddedHeaderIncludeTree");
|
||||
}
|
||||
|
||||
llvm::SmallVector<ValueDecl *, 1>
|
||||
TinyPtrVector<ValueDecl *>
|
||||
importer::getValueDeclsForName(NominalTypeDecl *decl, StringRef name) {
|
||||
// If the name is empty, don't try to find any decls.
|
||||
if (name.empty())
|
||||
@@ -8101,22 +8101,32 @@ importer::getValueDeclsForName(NominalTypeDecl *decl, StringRef name) {
|
||||
|
||||
auto &ctx = decl->getASTContext();
|
||||
auto clangDecl = decl->getClangDecl();
|
||||
llvm::SmallVector<ValueDecl *, 1> results;
|
||||
|
||||
if (name.consume_front(".")) {
|
||||
// Look for a member of decl instead of a global.
|
||||
if (name.empty())
|
||||
return {};
|
||||
|
||||
auto declName = DeclName(ctx.getIdentifier(name));
|
||||
auto swiftLookupResults = decl->lookupDirect(declName);
|
||||
if (!swiftLookupResults.empty())
|
||||
return SmallVector<ValueDecl *, 1>(swiftLookupResults.begin(),
|
||||
swiftLookupResults.end());
|
||||
auto allResults = evaluateOrDefault(
|
||||
ctx.evaluator, ClangRecordMemberLookup({decl, declName}), {});
|
||||
return SmallVector<ValueDecl *, 1>(allResults.begin(), allResults.end());
|
||||
NominalTypeDecl *searchDecl = decl;
|
||||
while (searchDecl) {
|
||||
auto swiftLookupResults = searchDecl->lookupDirect(declName);
|
||||
if (!swiftLookupResults.empty())
|
||||
return swiftLookupResults;
|
||||
|
||||
auto allResults = evaluateOrDefault(
|
||||
ctx.evaluator, ClangRecordMemberLookup({searchDecl, declName}), {});
|
||||
if (!allResults.empty())
|
||||
return allResults;
|
||||
|
||||
auto classDecl = dyn_cast<ClassDecl>(searchDecl);
|
||||
searchDecl = classDecl ? classDecl->getSuperclassDecl() : nullptr;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
SmallVector<ValueDecl *, 1> results;
|
||||
|
||||
auto *clangMod = clangDecl->getOwningModule();
|
||||
if (clangMod && clangMod->isSubModule())
|
||||
clangMod = clangMod->getTopLevelModule();
|
||||
@@ -8139,7 +8149,7 @@ importer::getValueDeclsForName(NominalTypeDecl *decl, StringRef name) {
|
||||
[&](ValueDecl *decl) { return !decl->getClangDecl(); });
|
||||
results.erase(newEnd, results.end());
|
||||
}
|
||||
return results;
|
||||
return TinyPtrVector<ValueDecl *>(ArrayRef(results));
|
||||
}
|
||||
|
||||
/// Is this a pointer or a reference to a foreign reference type.
|
||||
@@ -8739,8 +8749,7 @@ CustomRefCountingOperationResult CustomRefCountingOperation::evaluate(
|
||||
if (name == "immortal")
|
||||
return {CustomRefCountingOperationResult::immortal, nullptr, name};
|
||||
|
||||
llvm::SmallVector<ValueDecl *, 1> results =
|
||||
getValueDeclsForName(const_cast<ClassDecl*>(swiftDecl), name);
|
||||
auto results = getValueDeclsForName(const_cast<ClassDecl *>(swiftDecl), name);
|
||||
if (results.size() == 1)
|
||||
return {CustomRefCountingOperationResult::foundOperation, results.front(),
|
||||
name};
|
||||
|
||||
@@ -387,6 +387,17 @@ TinyPtrVector<ValueDecl *> ClangRecordMemberLookup::evaluate(
|
||||
for (const auto *valueDecl : result)
|
||||
foundMethodNames.insert(valueDecl->getName());
|
||||
|
||||
// If this FRT class has a single FRT superclass, skip looking up members
|
||||
// from that base: they are reachable via the Swift superclass chain
|
||||
// instead.
|
||||
const clang::RecordDecl *superclassClangDecl = nullptr;
|
||||
if (!inheritance) {
|
||||
auto derivedInfo = evaluateOrDefault(
|
||||
ctx.evaluator, ForeignReferenceTypeInfoRequest({cxxRecord}), {});
|
||||
if (auto primaryBase = derivedInfo.getPrimarySuperclass())
|
||||
superclassClangDecl = primaryBase;
|
||||
}
|
||||
|
||||
for (auto base : cxxRecord->bases()) {
|
||||
if (skipIfNonPublic && base.getAccessSpecifier() != clang::AS_public)
|
||||
continue;
|
||||
@@ -400,6 +411,10 @@ TinyPtrVector<ValueDecl *> ClangRecordMemberLookup::evaluate(
|
||||
|
||||
auto *baseRecord = baseType->getAs<clang::RecordType>()->getDecl();
|
||||
|
||||
if (superclassClangDecl && baseRecord->getCanonicalDecl() ==
|
||||
superclassClangDecl->getCanonicalDecl())
|
||||
continue;
|
||||
|
||||
if (importer::isSymbolicCircularBase(cxxRecord, baseRecord))
|
||||
// Skip circular bases to avoid unbounded recursion
|
||||
continue;
|
||||
@@ -806,6 +821,13 @@ ClangImporter::Implementation::lookupAndImportSubscripts(
|
||||
if (!R.has_value())
|
||||
return {};
|
||||
|
||||
// If this FRT class has a single FRT superclass, inherited overloads whose
|
||||
// declaring class is that superclass (or a Clang base of it) are reachable
|
||||
// via the Swift superclass chain and should not be synthesized here again.
|
||||
auto frtInfo = evaluateOrDefault(
|
||||
SwiftContext.evaluator, ForeignReferenceTypeInfoRequest({CXXRecord}), {});
|
||||
auto superclassClangDecl = frtInfo.getPrimarySuperclass();
|
||||
|
||||
llvm::SmallDenseMap<CXXOverloadArgTypes, std::pair<CXXOverload, CXXOverload>,
|
||||
1>
|
||||
CXXSubscripts;
|
||||
@@ -817,6 +839,14 @@ ClangImporter::Implementation::lookupAndImportSubscripts(
|
||||
overload.method->getRefQualifier() == clang::RQ_RValue)
|
||||
continue;
|
||||
|
||||
if (superclassClangDecl) {
|
||||
auto methodParent = overload.method->getParent();
|
||||
if (methodParent != CXXRecord &&
|
||||
(methodParent == superclassClangDecl ||
|
||||
superclassClangDecl->isDerivedFrom(methodParent)))
|
||||
continue;
|
||||
}
|
||||
|
||||
auto retTy = overload.method->getReturnType();
|
||||
auto isSetter = (retTy->isAnyPointerType() || retTy->isReferenceType()) &&
|
||||
!retTy->getPointeeType().isConstQualified();
|
||||
|
||||
@@ -2533,6 +2533,23 @@ namespace {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If this is an inherited foreign reference type, check if it has a
|
||||
// suitable superclass.
|
||||
auto frtInfo = evaluateOrDefault(
|
||||
Impl.SwiftContext.evaluator,
|
||||
ForeignReferenceTypeInfoRequest({cxxRecordDecl}), {});
|
||||
if (auto primaryBase = frtInfo.getPrimarySuperclass()) {
|
||||
if (auto baseDecl = cast_or_null<ClassDecl>(
|
||||
Impl.importDecl(primaryBase, getVersion()))) {
|
||||
auto classResult = cast<ClassDecl>(result);
|
||||
Type superclassType = baseDecl->getDeclaredInterfaceType();
|
||||
classResult->setSuperclass(superclassType);
|
||||
classResult->setInherited(
|
||||
Impl.SwiftContext.AllocateCopy(ArrayRef<InheritedEntry>{
|
||||
TypeLoc::withoutLoc(superclassType)}));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10768,6 +10785,11 @@ void ClangRecordMemberLoader::load(const clang::RecordDecl *clangRecord,
|
||||
const clang::CXXRecordDecl *cxxRecord;
|
||||
if ((cxxRecord = dyn_cast<clang::CXXRecordDecl>(clangRecord)) &&
|
||||
cxxRecord->isCompleteDefinition()) {
|
||||
auto derivedInfo =
|
||||
evaluateOrDefault(Impl.SwiftContext.evaluator,
|
||||
ForeignReferenceTypeInfoRequest({cxxRecord}), {});
|
||||
auto superclassClangDecl = derivedInfo.getPrimarySuperclass();
|
||||
|
||||
for (auto base : cxxRecord->bases()) {
|
||||
if (skipIfNonPublic && base.getAccessSpecifier() != clang::AS_public)
|
||||
continue;
|
||||
@@ -10782,6 +10804,13 @@ void ClangRecordMemberLoader::load(const clang::RecordDecl *clangRecord,
|
||||
continue;
|
||||
|
||||
auto *baseRecord = cast<clang::RecordType>(baseType)->getDecl();
|
||||
|
||||
// Skip cloning members from the base that is the Swift superclass;
|
||||
// those members are reachable through the superclass chain.
|
||||
if (superclassClangDecl && baseRecord->getCanonicalDecl() ==
|
||||
superclassClangDecl->getCanonicalDecl())
|
||||
continue;
|
||||
|
||||
auto baseInheritance = ClangInheritanceInfo(inheritance, base);
|
||||
load(baseRecord, baseInheritance);
|
||||
}
|
||||
|
||||
@@ -2281,8 +2281,8 @@ ImportedType findOptionSetEnum(clang::QualType type,
|
||||
/// and with the given name.
|
||||
///
|
||||
/// The name we're looking for is the Swift name.
|
||||
llvm::SmallVector<ValueDecl *, 1>
|
||||
getValueDeclsForName(NominalTypeDecl* decl, StringRef name);
|
||||
TinyPtrVector<ValueDecl *> getValueDeclsForName(NominalTypeDecl *decl,
|
||||
StringRef name);
|
||||
|
||||
template <typename T>
|
||||
const T *
|
||||
|
||||
@@ -235,7 +235,8 @@ namespace {
|
||||
|
||||
if (theClass->isNativeNSObjectSubclass()) {
|
||||
// For layout purposes, we don't have ObjC ancestry.
|
||||
} else if (theClass->hasSuperclass()) {
|
||||
} else if (theClass->hasSuperclass() &&
|
||||
!theClass->isForeignReferenceType()) {
|
||||
SILType superclassType = classType.getSuperclass();
|
||||
auto superclassDecl = superclassType.getClassOrBoundGenericClass();
|
||||
assert(superclassType && superclassDecl);
|
||||
|
||||
@@ -433,6 +433,22 @@ static bool willHaveConfusingConsumption(Type type,
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if this is an attempted downcast between two C++ foreign reference
|
||||
/// types, and diagnose if it is. This kind of downcasts is not supported yet.
|
||||
static bool diagnoseUnsupportedFRTDowncast(ASTContext &ctx, SourceLoc loc,
|
||||
Type fromType, Type toType) {
|
||||
auto fromUnderlying = fromType->lookThroughAllOptionalTypes();
|
||||
auto toUnderlying = toType->lookThroughAllOptionalTypes();
|
||||
if (fromUnderlying->isForeignReferenceType() &&
|
||||
toUnderlying->isForeignReferenceType() &&
|
||||
fromUnderlying->isBindableToSuperclassOf(toUnderlying)) {
|
||||
ctx.Diags.diagnose(loc, diag::downcast_to_foreign_reference_type, fromType,
|
||||
toType);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
/// Rewrites an expression by applying the solution of a constraint
|
||||
@@ -4192,6 +4208,10 @@ namespace {
|
||||
auto castKind = TypeChecker::typeCheckCheckedCast(
|
||||
fromType, toType, CheckedCastContextKind::IsExpr, dc);
|
||||
|
||||
if (diagnoseUnsupportedFRTDowncast(ctx, expr->getLoc(),
|
||||
fromType, toType))
|
||||
return nullptr;
|
||||
|
||||
switch (castKind) {
|
||||
case CheckedCastKind::Unresolved:
|
||||
expr->setCastKind(CheckedCastKind::ValueCast);
|
||||
@@ -4632,6 +4652,11 @@ namespace {
|
||||
|
||||
const auto castKind = TypeChecker::typeCheckCheckedCast(
|
||||
fromType, toType, CheckedCastContextKind::ForcedCast, dc);
|
||||
|
||||
if (diagnoseUnsupportedFRTDowncast(ctx, expr->getLoc(),
|
||||
fromType, toType))
|
||||
return nullptr;
|
||||
|
||||
switch (castKind) {
|
||||
/// Invalid cast.
|
||||
case CheckedCastKind::Unresolved:
|
||||
@@ -4709,6 +4734,11 @@ namespace {
|
||||
|
||||
auto castKind = TypeChecker::typeCheckCheckedCast(
|
||||
fromType, toType, CheckedCastContextKind::ConditionalCast, dc);
|
||||
|
||||
if (diagnoseUnsupportedFRTDowncast(ctx, expr->getLoc(),
|
||||
fromType, toType))
|
||||
return nullptr;
|
||||
|
||||
switch (castKind) {
|
||||
// Invalid cast.
|
||||
case CheckedCastKind::Unresolved:
|
||||
|
||||
+14
-8
@@ -8152,17 +8152,23 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind,
|
||||
|
||||
// Special implicit nominal conversions.
|
||||
if (!type1->is<LValueType>()) {
|
||||
// Array -> Array.
|
||||
if (desugar1->isArray() && desugar2->isArray()) {
|
||||
conversionsOrFixes.push_back(ConversionRestrictionKind::ArrayUpcast);
|
||||
// Dictionary -> Dictionary.
|
||||
// Array -> Array.
|
||||
auto elementType = type1->getArrayElementType();
|
||||
if (!elementType->isForeignReferenceType())
|
||||
conversionsOrFixes.push_back(ConversionRestrictionKind::ArrayUpcast);
|
||||
} else if (isDictionaryType(desugar1) && isDictionaryType(desugar2)) {
|
||||
conversionsOrFixes.push_back(
|
||||
ConversionRestrictionKind::DictionaryUpcast);
|
||||
// Set -> Set.
|
||||
// Dictionary -> Dictionary.
|
||||
auto keyValueTypes = *isDictionaryType(desugar1);
|
||||
if (!keyValueTypes.first->isForeignReferenceType() &&
|
||||
!keyValueTypes.second->isForeignReferenceType())
|
||||
conversionsOrFixes.push_back(
|
||||
ConversionRestrictionKind::DictionaryUpcast);
|
||||
} else if (isSetType(desugar1) && isSetType(desugar2)) {
|
||||
conversionsOrFixes.push_back(
|
||||
ConversionRestrictionKind::SetUpcast);
|
||||
// Set -> Set.
|
||||
auto elementType = *isSetType(desugar1);
|
||||
if (!elementType->isForeignReferenceType())
|
||||
conversionsOrFixes.push_back(ConversionRestrictionKind::SetUpcast);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2089,8 +2089,12 @@ TypeChecker::typeCheckCheckedCast(Type fromType, Type toType,
|
||||
return CheckedCastKind::ValueCast;
|
||||
|
||||
// Compare superclass bounds.
|
||||
if (fromSuperclass->isBindableToSuperclassOf(toSuperclass))
|
||||
if (fromSuperclass->isBindableToSuperclassOf(toSuperclass)) {
|
||||
if (toType->isForeignReferenceType() ||
|
||||
fromType->isForeignReferenceType())
|
||||
return failed();
|
||||
return CheckedCastKind::ValueCast;
|
||||
}
|
||||
|
||||
// An upcast is also OK.
|
||||
if (toSuperclass->isBindableToSuperclassOf(fromSuperclass))
|
||||
|
||||
@@ -112,3 +112,8 @@ module RefKit {
|
||||
header "refkit.hpp"
|
||||
requires cplusplus
|
||||
}
|
||||
|
||||
module Upcast {
|
||||
header "upcast.h"
|
||||
requires cplusplus
|
||||
}
|
||||
|
||||
@@ -0,0 +1,198 @@
|
||||
#define FRT_IMMORTAL \
|
||||
__attribute__((swift_attr("import_reference"))) \
|
||||
__attribute__((swift_attr("retain:immortal"))) \
|
||||
__attribute__((swift_attr("release:immortal")))
|
||||
|
||||
struct FRT_IMMORTAL Base {
|
||||
int baseValue;
|
||||
|
||||
Base() : baseValue(1) {}
|
||||
Base(const Base &) = delete;
|
||||
|
||||
int getBaseValue() const { return baseValue; }
|
||||
void setBaseValue(int v) { baseValue = v; }
|
||||
|
||||
static Base &create() {
|
||||
static Base instance;
|
||||
return instance;
|
||||
}
|
||||
};
|
||||
|
||||
struct FRT_IMMORTAL Derived : Base {
|
||||
int derivedValue;
|
||||
|
||||
Derived() : derivedValue(2) {}
|
||||
Derived(const Derived &) = delete;
|
||||
|
||||
int getDerivedValue() const { return derivedValue; }
|
||||
|
||||
static Derived &create() {
|
||||
static Derived instance;
|
||||
return instance;
|
||||
}
|
||||
};
|
||||
|
||||
struct FRT_IMMORTAL LeafDerived : Derived {
|
||||
int leafValue;
|
||||
|
||||
LeafDerived() : leafValue(3) {}
|
||||
LeafDerived(const LeafDerived &) = delete;
|
||||
|
||||
int getLeafValue() const { return leafValue; }
|
||||
|
||||
static LeafDerived &create() {
|
||||
static LeafDerived instance;
|
||||
return instance;
|
||||
}
|
||||
};
|
||||
|
||||
struct FRT_IMMORTAL Unrelated {
|
||||
int unrelatedValue;
|
||||
|
||||
Unrelated() : unrelatedValue(99) {}
|
||||
Unrelated(const Unrelated &) = delete;
|
||||
|
||||
static Unrelated &create() {
|
||||
static Unrelated instance;
|
||||
return instance;
|
||||
}
|
||||
};
|
||||
|
||||
inline int getBaseValueFromBase(const Base &b) { return b.getBaseValue(); }
|
||||
|
||||
// Virtual inheritance: should NOT produce a Swift superclass relationship.
|
||||
struct FRT_IMMORTAL VirtualDerived : virtual Base {
|
||||
int virtualDerivedValue;
|
||||
|
||||
VirtualDerived() : virtualDerivedValue(4) {}
|
||||
VirtualDerived(const VirtualDerived &) = delete;
|
||||
|
||||
int getVirtualDerivedValue() const { return virtualDerivedValue; }
|
||||
|
||||
static VirtualDerived &create() {
|
||||
static VirtualDerived instance;
|
||||
return instance;
|
||||
}
|
||||
};
|
||||
|
||||
struct RefCountedBase {
|
||||
int refCount = 1;
|
||||
int baseField = 10;
|
||||
|
||||
RefCountedBase() = default;
|
||||
RefCountedBase(const RefCountedBase &) = delete;
|
||||
|
||||
void retain() { refCount++; }
|
||||
void release() { refCount--; }
|
||||
|
||||
int getBaseValue() const { return baseField; }
|
||||
|
||||
static RefCountedBase &create() {
|
||||
static RefCountedBase instance;
|
||||
return instance;
|
||||
}
|
||||
} __attribute__((swift_attr("import_reference")))
|
||||
__attribute__((swift_attr("retain:.retain")))
|
||||
__attribute__((swift_attr("release:.release")));
|
||||
|
||||
struct RefCountedDerived : RefCountedBase {
|
||||
int derivedField = 20;
|
||||
|
||||
RefCountedDerived() = default;
|
||||
RefCountedDerived(const RefCountedDerived &) = delete;
|
||||
|
||||
int getDerivedField() const { return derivedField; }
|
||||
|
||||
static RefCountedDerived &create() {
|
||||
static RefCountedDerived instance;
|
||||
return instance;
|
||||
}
|
||||
};
|
||||
|
||||
inline int getBaseValueFromRefCountedBase(const RefCountedBase &b) {
|
||||
return b.getBaseValue();
|
||||
}
|
||||
|
||||
struct OverridesLifetimeOps : RefCountedDerived {
|
||||
int cVal = 3;
|
||||
void retainC() const {}
|
||||
void releaseC() const {}
|
||||
} __attribute__((swift_attr("import_reference")))
|
||||
__attribute__((swift_attr("retain:.retainC")))
|
||||
__attribute__((swift_attr("release:.releaseC")));
|
||||
|
||||
struct OverridesLifetimeOpsDerived : OverridesLifetimeOps {
|
||||
int dVal = 4;
|
||||
} __attribute__((swift_attr("import_reference")))
|
||||
__attribute__((swift_attr("retain:.retainC")))
|
||||
__attribute__((swift_attr("release:.releaseC")));
|
||||
// FIXME: this redeclares the lifetime operations, since ClangImporter currently
|
||||
// reports a conflict between annotations on base types otherwise
|
||||
|
||||
// Private inheritance: should NOT produce a Swift superclass relationship.
|
||||
struct FRT_IMMORTAL PrivateDerived : private Base {
|
||||
int privateDerivedValue;
|
||||
|
||||
PrivateDerived() : privateDerivedValue(5) {}
|
||||
PrivateDerived(const PrivateDerived &) = delete;
|
||||
|
||||
int getPrivateDerivedValue() const { return privateDerivedValue; }
|
||||
|
||||
static PrivateDerived &create() {
|
||||
static PrivateDerived instance;
|
||||
return instance;
|
||||
}
|
||||
};
|
||||
|
||||
// Protected inheritance: should NOT produce a Swift superclass relationship.
|
||||
struct FRT_IMMORTAL ProtectedDerived : protected Base {
|
||||
int protectedDerivedValue;
|
||||
|
||||
ProtectedDerived() : protectedDerivedValue(6) {}
|
||||
ProtectedDerived(const ProtectedDerived &) = delete;
|
||||
|
||||
int getProtectedDerivedValue() const { return protectedDerivedValue; }
|
||||
|
||||
static ProtectedDerived &create() {
|
||||
static ProtectedDerived instance;
|
||||
return instance;
|
||||
}
|
||||
};
|
||||
|
||||
struct EmptyTag {};
|
||||
|
||||
struct FRT_IMMORTAL DerivedFromEmptyAndBase : EmptyTag, Base {
|
||||
int extraValue;
|
||||
|
||||
DerivedFromEmptyAndBase() : extraValue(7) {}
|
||||
DerivedFromEmptyAndBase(const DerivedFromEmptyAndBase &) = delete;
|
||||
|
||||
int getExtraValue() const { return extraValue; }
|
||||
|
||||
static DerivedFromEmptyAndBase &create() {
|
||||
static DerivedFromEmptyAndBase instance;
|
||||
return instance;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Derived>
|
||||
struct FRT_IMMORTAL CRTPBase {
|
||||
int crtpBaseValue;
|
||||
|
||||
CRTPBase() : crtpBaseValue(10) {}
|
||||
|
||||
Derived *derivedSelf() { return static_cast<Derived *>(this); }
|
||||
};
|
||||
|
||||
struct FRT_IMMORTAL CRTPDerived : CRTPBase<CRTPDerived> {
|
||||
int crtpDerivedValue;
|
||||
|
||||
CRTPDerived() : crtpDerivedValue(11) {}
|
||||
|
||||
static CRTPDerived &create() {
|
||||
static CRTPDerived instance;
|
||||
return instance;
|
||||
}
|
||||
};
|
||||
|
||||
using CRTPBaseOfDerived = CRTPBase<CRTPDerived>;
|
||||
@@ -6,9 +6,8 @@
|
||||
// CHECK: func get42() -> Int32
|
||||
// CHECK: func getOverridden42() -> Int32
|
||||
// CHECK: }
|
||||
// CHECK: class Immortal {
|
||||
// CHECK: func getOverridden42() -> Int32
|
||||
// CHECK: func get42() -> Int32
|
||||
// CHECK: class Immortal : ImmortalBase {
|
||||
// CHECK: override func getOverridden42() -> Int32
|
||||
// CHECK: }
|
||||
|
||||
// CHECK: class Immortal2 {
|
||||
@@ -23,32 +22,30 @@
|
||||
// CHECK: final func swiftParamsRename(a1 i: Int32) -> Int32
|
||||
// CHECK: }
|
||||
|
||||
// CHECK: class B1 {
|
||||
// CHECK: final func virtualMethod() -> Int32
|
||||
// CHECK: final func swiftFooRename() -> Int32
|
||||
// CHECK: final func swiftBarRename() -> Int32
|
||||
// CHECK: final func swiftParamsRename(a1 i: Int32) -> Int32
|
||||
// CHECK: class B1 : A1 {
|
||||
// CHECK: final override func virtualMethod() -> Int32
|
||||
// CHECK: final override func swiftFooRename() -> Int32
|
||||
// CHECK: final override func swiftBarRename() -> Int32
|
||||
// CHECK: final override func swiftParamsRename(a1 i: Int32) -> Int32
|
||||
// CHECK: }
|
||||
|
||||
// CHECK: class B2 {
|
||||
// CHECK: final func virtualMethod() -> Int32
|
||||
// CHECK: final func swiftFooRename() -> Int32
|
||||
// CHECK: final func swiftBarRename() -> Int32
|
||||
// CHECK: final func swiftParamsRename(a1 i: Int32) -> Int32
|
||||
// CHECK: class B2 : A1 {
|
||||
// CHECK: final override func virtualMethod() -> Int32
|
||||
// CHECK: final override func swiftFooRename() -> Int32
|
||||
// CHECK: final override func swiftBarRename() -> Int32
|
||||
// CHECK: }
|
||||
|
||||
// CHECK: class C1 {
|
||||
// CHECK: final func swiftFooRename() -> Int32
|
||||
// CHECK: final func swiftBarRename() -> Int32
|
||||
// CHECK: final func swiftParamsRename(a1 i: Int32) -> Int32
|
||||
// CHECK: final func virtualMethod() -> Int32
|
||||
// CHECK: class C1 : B1 {
|
||||
// CHECK: final override func swiftFooRename() -> Int32
|
||||
// CHECK: final override func swiftBarRename() -> Int32
|
||||
// CHECK: final override func swiftParamsRename(a1 i: Int32) -> Int32
|
||||
// CHECK: }
|
||||
|
||||
// CHECK: class C2 {
|
||||
// CHECK: final func virtualMethod() -> Int32
|
||||
// CHECK: final func swiftFooRename() -> Int32
|
||||
// CHECK: final func swiftBarRename() -> Int32
|
||||
// CHECK: final func swiftParamsRename(a1 i: Int32) -> Int32
|
||||
// CHECK: class C2 : B1 {
|
||||
// CHECK: final override func virtualMethod() -> Int32
|
||||
// CHECK: final override func swiftFooRename() -> Int32
|
||||
// CHECK: final override func swiftBarRename() -> Int32
|
||||
// CHECK: final override func swiftParamsRename(a1 i: Int32) -> Int32
|
||||
// CHECK: }
|
||||
|
||||
// CHECK: class A2 {
|
||||
@@ -146,15 +143,11 @@
|
||||
// CHECK: final func pureRenameDerived() -> Int32
|
||||
// CHECK: }
|
||||
|
||||
// CHECK: class DerivedAbstractFRT {
|
||||
// CHECK: init()
|
||||
// CHECK: final func pureVirtualMethod() -> Int32
|
||||
// CHECK: final func swiftPureRenameBase() -> Int32
|
||||
// CHECK: final func pureRenameDerived() -> Int32
|
||||
// CHECK: class DerivedAbstractFRT : AbstractFRT {
|
||||
// CHECK: final override func pureVirtualMethod() -> Int32
|
||||
// CHECK: final override func swiftPureRenameBase() -> Int32
|
||||
// CHECK: final override func pureRenameDerived() -> Int32
|
||||
// CHECK: }
|
||||
|
||||
// CHECK: class EmptyDerivedAbstractFRT {
|
||||
// CHECK: final func pureVirtualMethod() -> Int32
|
||||
// CHECK: final func swiftPureRenameBase() -> Int32
|
||||
// CHECK: final func pureRenameDerived() -> Int32
|
||||
// CHECK: class EmptyDerivedAbstractFRT : AbstractFRT {
|
||||
// CHECK: }
|
||||
|
||||
@@ -4,9 +4,7 @@
|
||||
// CHECK: func doRetain()
|
||||
// CHECK: func doRelease()
|
||||
// CHECK: }
|
||||
// CHECK: class DerivedRefCountedBox {
|
||||
// CHECK: func doRetain()
|
||||
// CHECK: func doRelease()
|
||||
// CHECK: class DerivedRefCountedBox : RefCountedBox {
|
||||
// CHECK: }
|
||||
|
||||
// CHECK: class DerivedHasRelease {
|
||||
@@ -25,7 +23,7 @@
|
||||
// CHECK: func doRetainInBase()
|
||||
// CHECK: }
|
||||
|
||||
// CHECK: class CRTPDerived {
|
||||
// CHECK: class CRTPDerived : CRTPBase<CRTPDerived> {
|
||||
// CHECK: var value: Int32
|
||||
// CHECK: }
|
||||
|
||||
@@ -33,7 +31,7 @@
|
||||
// CHECK: func doRetainVirtual()
|
||||
// CHECK: func doReleaseVirtual()
|
||||
// CHECK: }
|
||||
// CHECK: class DerivedVirtualRetainRelease {
|
||||
// CHECK: class DerivedVirtualRetainRelease : VirtualRetainRelease {
|
||||
// CHECK: func doRetainVirtual()
|
||||
// CHECK: func doReleaseVirtual()
|
||||
// CHECK: }
|
||||
@@ -42,7 +40,7 @@
|
||||
// CHECK: func doRetainPure()
|
||||
// CHECK: func doReleasePure()
|
||||
// CHECK: }
|
||||
// CHECK: class DerivedPureVirtualRetainRelease {
|
||||
// CHECK: class DerivedPureVirtualRetainRelease : PureVirtualRetainRelease {
|
||||
// CHECK: func doRetainPure()
|
||||
// CHECK: func doReleasePure()
|
||||
// CHECK: var refCount: Int32
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
// RUN: %target-swift-emit-irgen %s -I %S/Inputs -cxx-interoperability-mode=default -validate-tbd-against-ir=none -disable-llvm-verify -Xcc -fignore-exceptions -disable-availability-checking | %FileCheck %s
|
||||
|
||||
import Upcast
|
||||
|
||||
public func passDerivedAsBase() -> Int32 {
|
||||
let d = Derived.create()
|
||||
return getBaseValueFromBase(d)
|
||||
}
|
||||
// CHECK-LABEL: define {{.*}}swiftcc i32 @"$s4main17passDerivedAsBases5Int32VyF"
|
||||
// CHECK: [[PTR:%[0-9]+]] = call ptr @{{_ZN7Derived6createEv|"\?create@Derived@@SAAEAU1@XZ"}}
|
||||
// CHECK: call {{.*}}i32 @{{_Z20getBaseValueFromBaseRK4Base|"\?getBaseValueFromBase@@YAHAEBUBase@@@Z"}}(ptr [[PTR]])
|
||||
|
||||
public func passRefCountedDerivedAsBase() -> Int32 {
|
||||
let d = RefCountedDerived.create()
|
||||
return getBaseValueFromRefCountedBase(d)
|
||||
}
|
||||
// CHECK-LABEL: define {{.*}}swiftcc i32 @"$s4main27passRefCountedDerivedAsBases5Int32VyF"
|
||||
// CHECK: [[RCPTR:%[0-9]+]] = call ptr @{{_ZN17RefCountedDerived6createEv|"\?create@RefCountedDerived@@SAAEAU1@XZ"}}
|
||||
// CHECK: call {{.*}}@{{_ZN17RefCountedDerived37__synthesized_lifetimeAccessor_retainEv|"\?__synthesized_lifetimeAccessor_retain@RefCountedDerived@@QEAAXXZ"}}(ptr [[RCPTR]])
|
||||
// CHECK: call {{.*}}i32 @{{_Z30getBaseValueFromRefCountedBaseRK14RefCountedBase|"\?getBaseValueFromRefCountedBase@@YAHAEBURefCountedBase@@@Z"}}(ptr [[RCPTR]])
|
||||
@@ -0,0 +1,62 @@
|
||||
// RUN: %target-swift-ide-test -print-module -module-to-print=Upcast -I %S/Inputs -I %swift_src_root/lib/ClangImporter/SwiftBridging -source-filename=x -cxx-interoperability-mode=upcoming-swift -print-implicit-attrs | %FileCheck %s
|
||||
|
||||
// CHECK: class Base {
|
||||
// CHECK: var baseValue: Int32
|
||||
// CHECK: func getBaseValue() -> Int32
|
||||
// CHECK: func setBaseValue(_ v: Int32)
|
||||
// CHECK: class func create() -> Base
|
||||
// CHECK: }
|
||||
|
||||
// CHECK: class Derived : Base {
|
||||
// CHECK: var derivedValue: Int32
|
||||
// CHECK: func getDerivedValue() -> Int32
|
||||
// CHECK: override class func create() -> Derived
|
||||
// CHECK: }
|
||||
|
||||
// CHECK: class LeafDerived : Derived {
|
||||
// CHECK: var leafValue: Int32
|
||||
// CHECK: func getLeafValue() -> Int32
|
||||
// CHECK: override class func create() -> LeafDerived
|
||||
// CHECK: }
|
||||
|
||||
// CHECK: class Unrelated {
|
||||
// CHECK: var unrelatedValue: Int32
|
||||
// CHECK: class func create() -> Unrelated
|
||||
// CHECK: }
|
||||
|
||||
// Virtual base: no Swift superclass relationship.
|
||||
// CHECK: class VirtualDerived {
|
||||
|
||||
// CHECK: class RefCountedBase {
|
||||
// CHECK: var baseField: Int32
|
||||
// CHECK: func getBaseValue() -> Int32
|
||||
// CHECK: class func create() -> RefCountedBase
|
||||
// CHECK: }
|
||||
|
||||
// CHECK: class RefCountedDerived : RefCountedBase {
|
||||
// CHECK: var derivedField: Int32
|
||||
// CHECK: func getDerivedField() -> Int32
|
||||
// CHECK: override class func create() -> RefCountedDerived
|
||||
// CHECK: }
|
||||
|
||||
// CHECK: class OverridesLifetimeOps {
|
||||
|
||||
// CHECK: class OverridesLifetimeOpsDerived : OverridesLifetimeOps {
|
||||
|
||||
// CHECK: class DerivedFromEmptyAndBase : Base {
|
||||
// CHECK: var extraValue: Int32
|
||||
// CHECK: func getExtraValue() -> Int32
|
||||
// CHECK: override class func create() -> DerivedFromEmptyAndBase
|
||||
// CHECK: }
|
||||
|
||||
// CHECK: class CRTPBase<CRTPDerived> {
|
||||
// CHECK: var crtpBaseValue: Int32
|
||||
// CHECK: func derivedSelf() -> CRTPDerived!
|
||||
// CHECK: }
|
||||
|
||||
// CHECK: class CRTPDerived : CRTPBase<CRTPDerived> {
|
||||
// CHECK: var crtpDerivedValue: Int32
|
||||
// CHECK: class func create() -> CRTPDerived
|
||||
// CHECK: }
|
||||
|
||||
// CHECK: typealias CRTPBaseOfDerived = CRTPBase<CRTPDerived>
|
||||
@@ -0,0 +1,172 @@
|
||||
// RUN: %target-typecheck-verify-swift -cxx-interoperability-mode=default -disable-availability-checking -I %S/Inputs
|
||||
|
||||
import Upcast
|
||||
|
||||
func upcastDerived(_ d: Derived) -> Base {
|
||||
return d as Base
|
||||
}
|
||||
|
||||
func upcastLeaf(_ l: LeafDerived) -> Derived {
|
||||
return l as Derived
|
||||
}
|
||||
|
||||
func upcastLeafToBase(_ l: LeafDerived) -> Base {
|
||||
return l as Base
|
||||
}
|
||||
|
||||
func takesBase(_ b: Base) -> Int32 {
|
||||
return b.getBaseValue()
|
||||
}
|
||||
func implicitConversion(_ d: Derived) -> Int32 {
|
||||
return takesBase(d)
|
||||
}
|
||||
func implicitConversionLeaf(_ l: LeafDerived) -> Int32 {
|
||||
return takesBase(l)
|
||||
}
|
||||
|
||||
// MARK: - Overridden lifetime operations:
|
||||
|
||||
func upcastOverridesToDerived(_ c: OverridesLifetimeOps) -> RefCountedDerived {
|
||||
return c as RefCountedDerived // expected-error {{cannot convert value of type 'OverridesLifetimeOps' to type 'RefCountedDerived' in coercion}}
|
||||
}
|
||||
|
||||
func upcastOverridesToBase(_ c: OverridesLifetimeOps) -> RefCountedBase {
|
||||
return c as RefCountedBase // expected-error {{cannot convert value of type 'OverridesLifetimeOps' to type 'RefCountedBase' in coercion}}
|
||||
}
|
||||
|
||||
func upcastDerivedToOverrides(_ d: OverridesLifetimeOpsDerived) -> OverridesLifetimeOps {
|
||||
return d as OverridesLifetimeOps
|
||||
}
|
||||
|
||||
func upcastDerivedToRefCountedDerived(_ d: OverridesLifetimeOpsDerived) -> RefCountedDerived {
|
||||
return d as RefCountedDerived // expected-error {{cannot convert value of type 'OverridesLifetimeOpsDerived' to type 'RefCountedDerived' in coercion}}
|
||||
}
|
||||
|
||||
func unrelatedCast(_ u: Unrelated) -> Base {
|
||||
return u as Base // expected-error {{cannot convert value of type 'Unrelated' to type 'Base' in coercion}}
|
||||
}
|
||||
|
||||
func virtualBaseCast(_ v: VirtualDerived) -> Base {
|
||||
return v as Base // expected-error {{cannot convert value of type 'VirtualDerived' to type 'Base' in coercion}}
|
||||
}
|
||||
|
||||
func privateBaseCast(_ p: PrivateDerived) -> Base {
|
||||
return p as Base // expected-error {{cannot convert value of type 'PrivateDerived' to type 'Base' in coercion}}
|
||||
}
|
||||
|
||||
func protectedBaseCast(_ p: ProtectedDerived) -> Base {
|
||||
return p as Base // expected-error {{cannot convert value of type 'ProtectedDerived' to type 'Base' in coercion}}
|
||||
}
|
||||
|
||||
func arrayUpcast(_ arr: [Derived]) -> [Base] {
|
||||
return arr as [Base] // expected-error {{cannot convert value of type '[Derived]' to type '[Base]' in coercion}}
|
||||
// expected-note@-1 {{arguments to generic parameter 'Element' ('Derived' and 'Base') are expected to be equal}}
|
||||
}
|
||||
|
||||
func arrayUpcastImplicit(_ arr: [Derived]) {
|
||||
let _: [Base] = arr // expected-error {{cannot assign value of type '[Derived]' to type '[Base]'}}
|
||||
// expected-note@-1 {{arguments to generic parameter 'Element' ('Derived' and 'Base') are expected to be equal}}
|
||||
}
|
||||
|
||||
/// This conformance is needed to let us write Set<Derived>.
|
||||
extension Base: @retroactive Hashable {
|
||||
public static func == (lhs: Base, rhs: Base) -> Bool {
|
||||
return lhs.getBaseValue() == rhs.getBaseValue()
|
||||
}
|
||||
public func hash(into hasher: inout Hasher) {
|
||||
hasher.combine(getBaseValue())
|
||||
}
|
||||
}
|
||||
|
||||
func setUpcast(_ s: Set<Derived>) -> Set<Base> {
|
||||
return s as Set<Base> // expected-error {{cannot convert value of type 'Set<Derived>' to type 'Set<Base>' in coercion}}
|
||||
// expected-note@-1 {{arguments to generic parameter 'Element' ('Derived' and 'Base') are expected to be equal}}
|
||||
}
|
||||
|
||||
func setUpcastImplicit(_ s: Set<Derived>) {
|
||||
let _: Set<Base> = s // expected-error {{cannot assign value of type 'Set<Derived>' to type 'Set<Base>'}}
|
||||
// expected-note@-1 {{arguments to generic parameter 'Element' ('Derived' and 'Base') are expected to be equal}}
|
||||
}
|
||||
|
||||
func castToEmptyTag(_ d: DerivedFromEmptyAndBase) -> EmptyTag {
|
||||
return d as EmptyTag // expected-error {{cannot convert value of type 'DerivedFromEmptyAndBase' to type 'EmptyTag' in coercion}}
|
||||
}
|
||||
|
||||
// MARK: - Upcasts with different optional levels:
|
||||
|
||||
func upcastOptionalToOptional(_ d: Derived?) -> Base? {
|
||||
return d as Base?
|
||||
}
|
||||
|
||||
func upcastToOptional(_ d: Derived) -> Base? {
|
||||
return d as Base?
|
||||
}
|
||||
|
||||
func upcastDoubleOptional(_ d: Derived??) -> Base?? {
|
||||
return d as Base??
|
||||
}
|
||||
|
||||
func upcastOptionalToDoubleOptional(_ d: Derived?) -> Base?? {
|
||||
return d as Base??
|
||||
}
|
||||
|
||||
func upcastImplicitOptional(_ d: Derived?) {
|
||||
let _: Base? = d
|
||||
}
|
||||
|
||||
func upcastImplicitDoubleOptional(_ d: Derived??) {
|
||||
let _: Base?? = d
|
||||
}
|
||||
|
||||
func upcastDoubleOptionalToSingleOptional(_ d: Derived??) -> Base? {
|
||||
return d as Base? // expected-error {{cannot convert value of type 'Derived??' to type 'Base?' in coercion}}
|
||||
// expected-note@-1 {{arguments to generic parameter 'Wrapped' ('Derived?' and 'Base') are expected to be equal}}
|
||||
}
|
||||
|
||||
// MARK: - Unsupported downcasts:
|
||||
|
||||
func downcast(_ b: Base) -> Derived? {
|
||||
return b as Derived // expected-error {{cannot convert value of type 'Base' to type 'Derived' in coercion}}
|
||||
}
|
||||
|
||||
func downcastConditional(_ b: Base) -> Derived? {
|
||||
return b as? Derived // expected-error {{downcast from 'Base' to 'Derived' is not supported for foreign reference types}}
|
||||
// expected-warning@-1 {{cast from 'Base' to unrelated type 'Derived' always fails}}
|
||||
}
|
||||
|
||||
func downcastForced(_ b: Base) -> Derived {
|
||||
return b as! Derived // expected-error {{downcast from 'Base' to 'Derived' is not supported for foreign reference types}}
|
||||
// expected-warning@-1 {{cast from 'Base' to unrelated type 'Derived' always fails}}
|
||||
}
|
||||
|
||||
func downcastIs(_ b: Base) -> Bool {
|
||||
return b is Derived // expected-error {{downcast from 'Base' to 'Derived' is not supported for foreign reference types}}
|
||||
// expected-warning@-1 {{cast from 'Base' to unrelated type 'Derived' always fails}}
|
||||
}
|
||||
|
||||
// MARK: - CRTP (derived inherits from template base specialized on itself):
|
||||
|
||||
func upcastCRTP(_ d: CRTPDerived) -> CRTPBaseOfDerived {
|
||||
return d as CRTPBaseOfDerived
|
||||
}
|
||||
|
||||
func takesCRTPBase(_ b: CRTPBaseOfDerived) -> Int32 {
|
||||
return b.crtpBaseValue
|
||||
}
|
||||
|
||||
func implicitConversionCRTP(_ d: CRTPDerived) -> Int32 {
|
||||
return takesCRTPBase(d)
|
||||
}
|
||||
|
||||
func crtpDerivedCallsBaseMethod(_ d: CRTPDerived) -> Int32 {
|
||||
return d.crtpBaseValue
|
||||
}
|
||||
|
||||
func crtpSelf(_ d: CRTPDerived) -> CRTPDerived? {
|
||||
return d.derivedSelf()
|
||||
}
|
||||
|
||||
func crtpDowncast(_ b: CRTPBaseOfDerived) -> CRTPDerived {
|
||||
return b as! CRTPDerived // expected-error {{downcast from 'CRTPBaseOfDerived' (aka 'CRTPBase<CRTPDerived>') to 'CRTPDerived' is not supported for foreign reference types}}
|
||||
// expected-warning@-1 {{cast from 'CRTPBaseOfDerived' (aka 'CRTPBase<CRTPDerived>') to unrelated type 'CRTPDerived' always fails}}
|
||||
}
|
||||
@@ -0,0 +1,238 @@
|
||||
// RUN: %target-run-simple-swift(-I %S/Inputs -cxx-interoperability-mode=default -Xfrontend -disable-availability-checking -Onone)
|
||||
|
||||
// REQUIRES: executable_test
|
||||
|
||||
import StdlibUnittest
|
||||
import Upcast
|
||||
|
||||
var UpcastTestSuite = TestSuite("Foreign Reference Type Upcast")
|
||||
|
||||
UpcastTestSuite.test("Derived as Base") {
|
||||
let d = Derived.create()
|
||||
let b: Base = d as Base
|
||||
expectEqual(b.getBaseValue(), 1)
|
||||
expectEqual(d.getDerivedValue(), 2)
|
||||
}
|
||||
|
||||
UpcastTestSuite.test("LeafDerived as Base") {
|
||||
let l = LeafDerived.create()
|
||||
let lb: Base = l as Base
|
||||
expectEqual(lb.getBaseValue(), 1)
|
||||
}
|
||||
|
||||
UpcastTestSuite.test("LeafDerived as Derived") {
|
||||
let l = LeafDerived.create()
|
||||
let ld: Derived = l as Derived
|
||||
expectEqual(ld.getDerivedValue(), 2)
|
||||
}
|
||||
|
||||
UpcastTestSuite.test("implicit conversion in function call") {
|
||||
let d = Derived.create()
|
||||
let l = LeafDerived.create()
|
||||
func takesBase(_ b: Base) -> Int32 { b.getBaseValue() }
|
||||
expectEqual(takesBase(d), 1)
|
||||
expectEqual(takesBase(l), 1)
|
||||
}
|
||||
|
||||
UpcastTestSuite.test("implicit conversion in return position") {
|
||||
func makeBase() -> Base {
|
||||
let d = Derived.create()
|
||||
d.setBaseValue(77)
|
||||
return d
|
||||
}
|
||||
let b = makeBase()
|
||||
expectEqual(b.getBaseValue(), 77)
|
||||
|
||||
b.setBaseValue(1)
|
||||
}
|
||||
|
||||
UpcastTestSuite.test("implicit conversion in return position refcounted") {
|
||||
func makeRefBase() -> RefCountedBase {
|
||||
return RefCountedDerived.create()
|
||||
}
|
||||
let b = makeRefBase()
|
||||
expectEqual(b.getBaseValue(), 10)
|
||||
|
||||
expectTrue(b.refCount > 0)
|
||||
expectTrue(b.refCount < 10)
|
||||
}
|
||||
|
||||
UpcastTestSuite.test("RefCountedDerived as RefCountedBase") {
|
||||
let d = RefCountedDerived.create()
|
||||
expectTrue(d is RefCountedBase)
|
||||
|
||||
let b = d as RefCountedBase
|
||||
expectEqual(b.getBaseValue(), 10)
|
||||
expectEqual(d.getDerivedField(), 20)
|
||||
|
||||
expectTrue(d.refCount > 0)
|
||||
expectTrue(d.refCount < 10)
|
||||
}
|
||||
|
||||
UpcastTestSuite.test("refcounted implicit conversion") {
|
||||
func takesRefBase(_ b: RefCountedBase) -> Int32 { b.getBaseValue() }
|
||||
|
||||
let d = RefCountedDerived.create()
|
||||
expectEqual(takesRefBase(d), 10)
|
||||
|
||||
expectTrue(d.refCount > 0)
|
||||
expectTrue(d.refCount < 10)
|
||||
}
|
||||
|
||||
UpcastTestSuite.test("mutation through upcasted reference") {
|
||||
let d = Derived.create()
|
||||
let b = d as Base
|
||||
b.setBaseValue(42)
|
||||
defer { b.setBaseValue(1) }
|
||||
|
||||
expectEqual(b.getBaseValue(), 42)
|
||||
expectEqual(d.getBaseValue(), 42)
|
||||
}
|
||||
|
||||
UpcastTestSuite.test("Derived? as Base?") {
|
||||
let d: Derived? = Derived.create()
|
||||
let b: Base? = d
|
||||
expectNotNil(b)
|
||||
expectEqual(b!.getBaseValue(), 1)
|
||||
}
|
||||
|
||||
UpcastTestSuite.test("Derived?? as Base??") {
|
||||
let d: Derived?? = Derived.create()
|
||||
let b: Base?? = d
|
||||
expectEqual(b!!.getBaseValue(), 1)
|
||||
}
|
||||
|
||||
UpcastTestSuite.test("nil as? Base") {
|
||||
let d: Derived? = nil
|
||||
let b: Base? = d
|
||||
expectNil(b)
|
||||
}
|
||||
|
||||
UpcastTestSuite.test("RefCountedDerived? as RefCountedBase?") {
|
||||
let d: RefCountedDerived? = RefCountedDerived.create()
|
||||
let b: RefCountedBase? = d
|
||||
expectNotNil(b)
|
||||
expectEqual(b!.getBaseValue(), 10)
|
||||
|
||||
expectTrue(d!.refCount > 0)
|
||||
expectTrue(d!.refCount < 10)
|
||||
}
|
||||
|
||||
UpcastTestSuite.test("RefCountedDerived?? as RefCountedBase??") {
|
||||
let d: RefCountedDerived?? = RefCountedDerived.create()
|
||||
let b: RefCountedBase?? = d
|
||||
expectEqual(b!!.getBaseValue(), 10)
|
||||
|
||||
expectTrue(d!!.refCount > 0)
|
||||
expectTrue(d!!.refCount < 10)
|
||||
}
|
||||
|
||||
UpcastTestSuite.test("nil as? RefCountedBase") {
|
||||
let d: RefCountedDerived? = nil
|
||||
let b: RefCountedBase? = d
|
||||
expectNil(b)
|
||||
}
|
||||
|
||||
UpcastTestSuite.test("ternary type unification") {
|
||||
let d = Derived.create()
|
||||
let b = Base.create()
|
||||
d.setBaseValue(123)
|
||||
defer { d.setBaseValue(1) }
|
||||
|
||||
let r1 = true ? d : b
|
||||
expectEqual(r1.getBaseValue(), 123)
|
||||
|
||||
let r2 = false ? d : b
|
||||
expectEqual(r2.getBaseValue(), 1)
|
||||
}
|
||||
|
||||
UpcastTestSuite.test("ternary refcounted type unification") {
|
||||
let d = RefCountedDerived.create()
|
||||
let b = RefCountedBase.create()
|
||||
let r = true ? d : b
|
||||
expectEqual(r.getBaseValue(), 10)
|
||||
|
||||
expectTrue(d.refCount > 0)
|
||||
expectTrue(d.refCount < 10)
|
||||
}
|
||||
|
||||
UpcastTestSuite.test("heterogeneous array literal") {
|
||||
let d = Derived.create()
|
||||
let l = LeafDerived.create()
|
||||
d.setBaseValue(50)
|
||||
l.setBaseValue(60)
|
||||
defer { d.setBaseValue(1) }
|
||||
defer { l.setBaseValue(1) }
|
||||
|
||||
let arr: [Base] = [d, l]
|
||||
expectEqual(arr.count, 2)
|
||||
expectEqual(arr[0].getBaseValue(), 50)
|
||||
expectEqual(arr[1].getBaseValue(), 60)
|
||||
}
|
||||
|
||||
UpcastTestSuite.test("heterogeneous array literal refcounted") {
|
||||
let d = RefCountedDerived.create()
|
||||
let b = RefCountedBase.create()
|
||||
let arr: [RefCountedBase] = [d, b]
|
||||
expectEqual(arr.count, 2)
|
||||
expectEqual(arr[0].getBaseValue(), 10)
|
||||
expectEqual(arr[1].getBaseValue(), 10)
|
||||
|
||||
expectTrue(d.refCount > 0)
|
||||
expectTrue(d.refCount < 10)
|
||||
}
|
||||
|
||||
protocol Describable {
|
||||
func getBaseValue() -> Int32
|
||||
}
|
||||
|
||||
extension Base: Describable {}
|
||||
|
||||
UpcastTestSuite.test("protocol conformance inherited") {
|
||||
let d = Derived.create()
|
||||
let p: Describable = d
|
||||
expectEqual(p.getBaseValue(), 1)
|
||||
}
|
||||
|
||||
UpcastTestSuite.test("protocol conformance inherited deeply") {
|
||||
let l = LeafDerived.create()
|
||||
let p: Describable = l
|
||||
expectEqual(p.getBaseValue(), 1)
|
||||
}
|
||||
|
||||
UpcastTestSuite.test("[Protocol]") {
|
||||
let b = Base.create()
|
||||
let d = Derived.create()
|
||||
let l = LeafDerived.create()
|
||||
let arr: [Describable] = [b, d, l]
|
||||
expectEqual(arr.count, 3)
|
||||
for item in arr {
|
||||
expectEqual(item.getBaseValue(), 1)
|
||||
}
|
||||
}
|
||||
|
||||
extension RefCountedBase: Describable {}
|
||||
|
||||
UpcastTestSuite.test("protocol conformance inherited refcounted") {
|
||||
let d = RefCountedDerived.create()
|
||||
let p: Describable = d
|
||||
expectEqual(p.getBaseValue(), 10)
|
||||
|
||||
expectTrue(d.refCount > 0)
|
||||
expectTrue(d.refCount < 10)
|
||||
}
|
||||
|
||||
UpcastTestSuite.test("DerivedFromEmptyAndBase as Base") {
|
||||
let d = DerivedFromEmptyAndBase.create()
|
||||
let b: Base = d as Base
|
||||
expectEqual(b.getBaseValue(), 1)
|
||||
expectEqual(d.getExtraValue(), 7)
|
||||
}
|
||||
|
||||
UpcastTestSuite.test("DerivedFromEmptyAndBase implicit conversion") {
|
||||
func takesBase(_ b: Base) -> Int32 { b.getBaseValue() }
|
||||
let d = DerivedFromEmptyAndBase.create()
|
||||
expectEqual(takesBase(d), 1)
|
||||
}
|
||||
|
||||
runAllTests()
|
||||
Reference in New Issue
Block a user