[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:
Egor Zhdan
2026-04-27 16:57:50 +01:00
parent e2b178c44a
commit 6c464579db
19 changed files with 959 additions and 71 deletions
+3
View File
@@ -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 {
+79 -2
View File
@@ -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,
+21 -12
View File
@@ -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};
+30
View File
@@ -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();
+29
View File
@@ -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);
}
+2 -2
View File
@@ -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 *
+2 -1
View File
@@ -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);
+30
View File
@@ -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
View File
@@ -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);
}
}
}
+5 -1
View File
@@ -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()