mirror of
https://github.com/apple/swift.git
synced 2026-03-04 18:24:35 +01:00
[cxx-interop] Rudimentary support for importing base classes.
This commit adds very basic support for importing and calling base class methods, getting and setting base class fields, and using types inside of base classes.
This commit is contained in:
@@ -268,7 +268,7 @@ public:
|
||||
clang::DeclarationName givenName = clang::DeclarationName()) = 0;
|
||||
|
||||
/// Determine the effective Clang context for the given Swift nominal type.
|
||||
EffectiveClangContext virtual getEffectiveClangContext(
|
||||
virtual EffectiveClangContext getEffectiveClangContext(
|
||||
const NominalTypeDecl *nominal) = 0;
|
||||
};
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include "ClangDiagnosticConsumer.h"
|
||||
#include "ImporterImpl.h"
|
||||
#include "swift/AST/ASTContext.h"
|
||||
#include "swift/AST/Builtins.h"
|
||||
#include "swift/AST/ClangModuleLoader.h"
|
||||
#include "swift/AST/DiagnosticEngine.h"
|
||||
#include "swift/AST/DiagnosticsClangImporter.h"
|
||||
@@ -4350,6 +4351,416 @@ TinyPtrVector<ValueDecl *> CXXNamespaceMemberLookup::evaluate(
|
||||
return result;
|
||||
}
|
||||
|
||||
// Just create a specialized function decl for "__swift_interopStaticCast"
|
||||
// using the types base and derived.
|
||||
DeclRefExpr *getInteropStaticCastDeclRefExpr(ASTContext &ctx,
|
||||
const clang::Module *owningModule,
|
||||
Type base, Type derived) {
|
||||
// Lookup our static cast helper function.
|
||||
// TODO: change this to stdlib or something.
|
||||
auto wrapperModule =
|
||||
ctx.getClangModuleLoader()->getWrapperForModule(owningModule);
|
||||
SmallVector<ValueDecl *, 1> results;
|
||||
ctx.lookupInModule(wrapperModule, "__swift_interopStaticCast", results);
|
||||
assert(
|
||||
results.size() == 1 &&
|
||||
"Did you forget to define a __swift_interopStaticCast helper function?");
|
||||
FuncDecl *staticCastFn = cast<FuncDecl>(results.back());
|
||||
|
||||
// Now we have to force instanciate this. We can't let the type checker do
|
||||
// this yet because it can't infer the "To" type.
|
||||
auto subst =
|
||||
SubstitutionMap::get(staticCastFn->getGenericSignature(), {derived, base},
|
||||
ArrayRef<ProtocolConformanceRef>());
|
||||
auto functionTemplate = const_cast<clang::FunctionTemplateDecl *>(
|
||||
cast<clang::FunctionTemplateDecl>(staticCastFn->getClangDecl()));
|
||||
auto spec = ctx.getClangModuleLoader()->instantiateCXXFunctionTemplate(
|
||||
ctx, functionTemplate, subst);
|
||||
auto specializedStaticCastFn =
|
||||
cast<FuncDecl>(ctx.getClangModuleLoader()->importDeclDirectly(spec));
|
||||
|
||||
auto staticCastRefExpr = new (ctx)
|
||||
DeclRefExpr(ConcreteDeclRef(specializedStaticCastFn), DeclNameLoc(),
|
||||
/*implicit*/ true);
|
||||
staticCastRefExpr->setType(specializedStaticCastFn->getInterfaceType());
|
||||
|
||||
return staticCastRefExpr;
|
||||
}
|
||||
|
||||
// Create the following expressions:
|
||||
// %0 = Builtin.addressof(&self)
|
||||
// %1 = Builtin.reinterpretCast<UnsafeMutablePointer<Derived>>(%0)
|
||||
// %2 = __swift_interopStaticCast<UnsafeMutablePointer<Base>?>(%1)
|
||||
// %3 = %2!
|
||||
// return %3.pointee
|
||||
MemberRefExpr *getInOutSelfInteropStaticCast(FuncDecl *funcDecl,
|
||||
StructDecl *baseStruct,
|
||||
StructDecl *derivedStruct) {
|
||||
auto &ctx = funcDecl->getASTContext();
|
||||
|
||||
auto inoutSelf = [&ctx](FuncDecl *funcDecl) {
|
||||
auto inoutSelfDecl = funcDecl->getImplicitSelfDecl();
|
||||
|
||||
auto inoutSelfRef =
|
||||
new (ctx) DeclRefExpr(inoutSelfDecl, DeclNameLoc(), /*implicit*/ true);
|
||||
inoutSelfRef->setType(LValueType::get(inoutSelfDecl->getInterfaceType()));
|
||||
|
||||
auto inoutSelf = new (ctx) InOutExpr(
|
||||
SourceLoc(), inoutSelfRef,
|
||||
funcDecl->mapTypeIntoContext(inoutSelfDecl->getValueInterfaceType()),
|
||||
/*implicit*/ true);
|
||||
inoutSelf->setType(InOutType::get(inoutSelfDecl->getInterfaceType()));
|
||||
|
||||
return inoutSelf;
|
||||
}(funcDecl);
|
||||
|
||||
auto createCallToBuiltin = [&](Identifier name, ArrayRef<Type> substTypes,
|
||||
Expr *arg) {
|
||||
auto builtinFn = cast<FuncDecl>(getBuiltinValueDecl(ctx, name));
|
||||
auto substMap =
|
||||
SubstitutionMap::get(builtinFn->getGenericSignature(), substTypes,
|
||||
ArrayRef<ProtocolConformanceRef>());
|
||||
ConcreteDeclRef builtinFnRef(builtinFn, substMap);
|
||||
auto builtinFnRefExpr =
|
||||
new (ctx) DeclRefExpr(builtinFnRef, DeclNameLoc(), /*implicit*/ true);
|
||||
|
||||
auto fnType = builtinFn->getInterfaceType();
|
||||
if (auto genericFnType = dyn_cast<GenericFunctionType>(fnType.getPointer()))
|
||||
fnType = genericFnType->substGenericArgs(substMap);
|
||||
builtinFnRefExpr->setType(fnType);
|
||||
auto *argList = ArgumentList::forImplicitUnlabeled(ctx, {arg});
|
||||
auto callExpr = CallExpr::create(ctx, builtinFnRefExpr, argList, /*implicit*/ true);
|
||||
callExpr->setThrows(false);
|
||||
return callExpr;
|
||||
};
|
||||
|
||||
auto rawSelfPointer =
|
||||
createCallToBuiltin(ctx.getIdentifier("addressof"),
|
||||
{derivedStruct->getSelfInterfaceType()}, inoutSelf);
|
||||
rawSelfPointer->setType(ctx.TheRawPointerType);
|
||||
|
||||
auto derivedPtrType = derivedStruct->getSelfInterfaceType()->wrapInPointer(
|
||||
PTK_UnsafeMutablePointer);
|
||||
auto selfPointer = createCallToBuiltin(
|
||||
ctx.getIdentifier("reinterpretCast"),
|
||||
{ctx.TheRawPointerType, derivedPtrType}, rawSelfPointer);
|
||||
selfPointer->setType(derivedPtrType);
|
||||
|
||||
auto staticCastRefExpr = getInteropStaticCastDeclRefExpr(
|
||||
ctx, baseStruct->getClangDecl()->getOwningModule(),
|
||||
baseStruct->getSelfInterfaceType()->wrapInPointer(
|
||||
PTK_UnsafeMutablePointer),
|
||||
derivedStruct->getSelfInterfaceType()->wrapInPointer(
|
||||
PTK_UnsafeMutablePointer));
|
||||
auto *argList = ArgumentList::forImplicitUnlabeled(ctx, {selfPointer});
|
||||
auto casted = CallExpr::createImplicit(ctx, staticCastRefExpr, argList);
|
||||
// This will be "Optional<UnsafeMutablePointer<Base>>"
|
||||
casted->setType(cast<FunctionType>(staticCastRefExpr->getType().getPointer())
|
||||
->getResult());
|
||||
casted->setThrows(false);
|
||||
|
||||
// Now force unwrap the casted pointer.
|
||||
auto unwrapped = new (ctx) ForceValueExpr(casted, SourceLoc());
|
||||
unwrapped->setType(baseStruct->getSelfInterfaceType()->wrapInPointer(
|
||||
PTK_UnsafeMutablePointer));
|
||||
|
||||
SubstitutionMap pointeeSubst = SubstitutionMap::get(
|
||||
ctx.getUnsafeMutablePointerDecl()->getGenericSignature(),
|
||||
{baseStruct->getSelfInterfaceType()}, {});
|
||||
VarDecl *pointeePropertyDecl =
|
||||
ctx.getPointerPointeePropertyDecl(PTK_UnsafeMutablePointer);
|
||||
auto pointeePropertyRefExpr = new (ctx) MemberRefExpr(
|
||||
unwrapped, SourceLoc(),
|
||||
ConcreteDeclRef(pointeePropertyDecl, pointeeSubst), DeclNameLoc(),
|
||||
/*implicit=*/true);
|
||||
pointeePropertyRefExpr->setType(
|
||||
LValueType::get(baseStruct->getSelfInterfaceType()));
|
||||
|
||||
return pointeePropertyRefExpr;
|
||||
}
|
||||
|
||||
// For const methods generate the following:
|
||||
// %0 = __swift_interopStaticCast<Base>(self)
|
||||
// return %0.fn(args...)
|
||||
// For mutating methods we have to pass self as a pointer:
|
||||
// %0 = Builtin.addressof(&self)
|
||||
// %1 = Builtin.reinterpretCast<UnsafeMutablePointer<Derived>>(%0)
|
||||
// %2 = __swift_interopStaticCast<UnsafeMutablePointer<Base>?>(%1)
|
||||
// %3 = %2!
|
||||
// %4 = %3.pointee
|
||||
// return %4.fn(args...)
|
||||
static std::pair<BraceStmt *, bool>
|
||||
synthesizeBaseClassMethodBody(AbstractFunctionDecl *afd, void *context) {
|
||||
ASTContext &ctx = afd->getASTContext();
|
||||
|
||||
auto funcDecl = cast<FuncDecl>(afd);
|
||||
auto derivedStruct =
|
||||
cast<StructDecl>(funcDecl->getDeclContext()->getAsDecl());
|
||||
auto baseMember = static_cast<FuncDecl *>(context);
|
||||
auto baseStruct = cast<StructDecl>(baseMember->getDeclContext()->getAsDecl());
|
||||
auto baseType = baseStruct->getDeclaredType();
|
||||
|
||||
SmallVector<Expr *, 8> forwardingParams;
|
||||
for (auto param : *funcDecl->getParameters()) {
|
||||
auto paramRefExpr = new (ctx) DeclRefExpr(param, DeclNameLoc(),
|
||||
/*Implicit=*/true);
|
||||
paramRefExpr->setType(param->getType());
|
||||
forwardingParams.push_back(paramRefExpr);
|
||||
}
|
||||
|
||||
Expr *casted = nullptr;
|
||||
if (funcDecl->isMutating()) {
|
||||
auto pointeeMemberRefExpr =
|
||||
getInOutSelfInteropStaticCast(funcDecl, baseStruct, derivedStruct);
|
||||
casted = new (ctx) InOutExpr(SourceLoc(), pointeeMemberRefExpr, baseType,
|
||||
/*implicit*/ true);
|
||||
casted->setType(InOutType::get(baseType));
|
||||
} else {
|
||||
auto *selfDecl = funcDecl->getImplicitSelfDecl();
|
||||
auto selfExpr = new (ctx) DeclRefExpr(selfDecl, DeclNameLoc(),
|
||||
/*implicit*/ true);
|
||||
selfExpr->setType(selfDecl->getType());
|
||||
|
||||
auto staticCastRefExpr = getInteropStaticCastDeclRefExpr(
|
||||
ctx, baseStruct->getClangDecl()->getOwningModule(), baseType,
|
||||
derivedStruct->getDeclaredType());
|
||||
|
||||
auto *argList = ArgumentList::forImplicitUnlabeled(ctx, {selfExpr});
|
||||
auto castedCall = CallExpr::createImplicit(ctx, staticCastRefExpr, argList);
|
||||
castedCall->setType(baseType);
|
||||
castedCall->setThrows(false);
|
||||
casted = castedCall;
|
||||
}
|
||||
|
||||
auto *baseMemberExpr =
|
||||
new (ctx) DeclRefExpr(ConcreteDeclRef(baseMember), DeclNameLoc(),
|
||||
/*Implicit=*/true);
|
||||
baseMemberExpr->setType(baseMember->getInterfaceType());
|
||||
|
||||
auto baseMemberDotCallExpr =
|
||||
DotSyntaxCallExpr::create(ctx, baseMemberExpr, SourceLoc(), casted);
|
||||
baseMemberDotCallExpr->setType(baseMember->getMethodInterfaceType());
|
||||
baseMemberDotCallExpr->setThrows(false);
|
||||
|
||||
auto *argList = ArgumentList::forImplicitUnlabeled(ctx, forwardingParams);
|
||||
auto *baseMemberCallExpr = CallExpr::createImplicit(
|
||||
ctx, baseMemberDotCallExpr, argList);
|
||||
baseMemberCallExpr->setType(baseMember->getResultInterfaceType());
|
||||
baseMemberCallExpr->setThrows(false);
|
||||
|
||||
auto returnStmt = new (ctx) ReturnStmt(SourceLoc(), baseMemberCallExpr,
|
||||
/*implicit=*/true);
|
||||
|
||||
auto body = BraceStmt::create(ctx, SourceLoc(), {returnStmt}, SourceLoc(),
|
||||
/*implicit=*/true);
|
||||
return {body, /*isTypeChecked=*/true};
|
||||
}
|
||||
|
||||
// Getters are relatively easy. Just cast and return the member:
|
||||
// %0 = __swift_interopStaticCast<Base>(self)
|
||||
// return %0.member
|
||||
static std::pair<BraceStmt *, bool>
|
||||
synthesizeBaseClassFieldGetterBody(AbstractFunctionDecl *afd, void *context) {
|
||||
ASTContext &ctx = afd->getASTContext();
|
||||
|
||||
AccessorDecl *getterDecl = cast<AccessorDecl>(afd);
|
||||
VarDecl *baseClassVar = static_cast<VarDecl *>(context);
|
||||
StructDecl *baseStruct =
|
||||
cast<StructDecl>(baseClassVar->getDeclContext()->getAsDecl());
|
||||
StructDecl *derivedStruct =
|
||||
cast<StructDecl>(getterDecl->getDeclContext()->getAsDecl());
|
||||
|
||||
auto selfDecl = getterDecl->getImplicitSelfDecl();
|
||||
auto selfExpr = new (ctx) DeclRefExpr(selfDecl, DeclNameLoc(),
|
||||
/*implicit*/ true);
|
||||
selfExpr->setType(selfDecl->getType());
|
||||
|
||||
auto staticCastRefExpr = getInteropStaticCastDeclRefExpr(
|
||||
ctx, baseStruct->getClangDecl()->getOwningModule(),
|
||||
baseStruct->getSelfInterfaceType(),
|
||||
derivedStruct->getSelfInterfaceType());
|
||||
|
||||
auto *argList = ArgumentList::forImplicitUnlabeled(ctx, {selfExpr});
|
||||
auto casted = CallExpr::createImplicit(ctx, staticCastRefExpr, argList);
|
||||
casted->setType(baseStruct->getSelfInterfaceType());
|
||||
casted->setThrows(false);
|
||||
|
||||
// If the base class var has a clang decl, that means it's an access into a
|
||||
// stored field. Otherwise, we're looking into another base class, so it's a
|
||||
// another synthesized accessor.
|
||||
AccessSemantics accessKind = baseClassVar->getClangDecl()
|
||||
? AccessSemantics::DirectToStorage
|
||||
: AccessSemantics::DirectToImplementation;
|
||||
auto baseMember =
|
||||
new (ctx) MemberRefExpr(casted, SourceLoc(), baseClassVar, DeclNameLoc(),
|
||||
/*Implicit=*/true, accessKind);
|
||||
baseMember->setType(baseClassVar->getType());
|
||||
|
||||
auto ret = new (ctx) ReturnStmt(SourceLoc(), baseMember);
|
||||
auto body = BraceStmt::create(ctx, SourceLoc(), {ret}, SourceLoc(),
|
||||
/*implicit*/ true);
|
||||
return {body, /*isTypeChecked=*/true};
|
||||
}
|
||||
|
||||
// For setters we have to pass self as a pointer and then emit an assign:
|
||||
// %0 = Builtin.addressof(&self)
|
||||
// %1 = Builtin.reinterpretCast<UnsafeMutablePointer<Derived>>(%0)
|
||||
// %2 = __swift_interopStaticCast<UnsafeMutablePointer<Base>?>(%1)
|
||||
// %3 = %2!
|
||||
// %4 = %3.pointee
|
||||
// assign newValue to %4
|
||||
static std::pair<BraceStmt *, bool>
|
||||
synthesizeBaseClassFieldSetterBody(AbstractFunctionDecl *afd, void *context) {
|
||||
auto setterDecl = cast<AccessorDecl>(afd);
|
||||
VarDecl *baseClassVar = static_cast<VarDecl *>(context);
|
||||
ASTContext &ctx = setterDecl->getASTContext();
|
||||
|
||||
StructDecl *baseStruct =
|
||||
cast<StructDecl>(baseClassVar->getDeclContext()->getAsDecl());
|
||||
StructDecl *derivedStruct =
|
||||
cast<StructDecl>(setterDecl->getDeclContext()->getAsDecl());
|
||||
|
||||
auto *pointeePropertyRefExpr =
|
||||
getInOutSelfInteropStaticCast(setterDecl, baseStruct, derivedStruct);
|
||||
|
||||
// If the base class var has a clang decl, that means it's an access into a
|
||||
// stored field. Otherwise, we're looking into another base class, so it's a
|
||||
// another synthesized accessor.
|
||||
AccessSemantics accessKind = baseClassVar->getClangDecl()
|
||||
? AccessSemantics::DirectToStorage
|
||||
: AccessSemantics::DirectToImplementation;
|
||||
auto storedRef =
|
||||
new (ctx) MemberRefExpr(pointeePropertyRefExpr, SourceLoc(), baseClassVar,
|
||||
DeclNameLoc(), /*Implicit=*/true, accessKind);
|
||||
storedRef->setType(LValueType::get(baseClassVar->getType()));
|
||||
|
||||
auto newValueParamRefExpr =
|
||||
new (ctx) DeclRefExpr(setterDecl->getParameters()->get(0), DeclNameLoc(),
|
||||
/*Implicit=*/true);
|
||||
newValueParamRefExpr->setType(setterDecl->getParameters()->get(0)->getType());
|
||||
|
||||
auto assignExpr =
|
||||
new (ctx) AssignExpr(storedRef, SourceLoc(), newValueParamRefExpr,
|
||||
/*implicit*/ true);
|
||||
assignExpr->setType(TupleType::getEmpty(ctx));
|
||||
|
||||
auto body = BraceStmt::create(ctx, SourceLoc(), {assignExpr}, SourceLoc(),
|
||||
/*implicit*/ true);
|
||||
return {body, /*isTypeChecked=*/true};
|
||||
}
|
||||
|
||||
static std::array<AccessorDecl *, 2>
|
||||
makeBaseClassFieldAccessors(DeclContext *declContext, VarDecl *computedVar,
|
||||
VarDecl *baseClassVar) {
|
||||
auto &ctx = declContext->getASTContext();
|
||||
auto computedType = computedVar->getInterfaceType();
|
||||
|
||||
auto getterDecl = AccessorDecl::create(
|
||||
ctx,
|
||||
/*FuncLoc=*/SourceLoc(),
|
||||
/*AccessorKeywordLoc=*/SourceLoc(), AccessorKind::Get, computedVar,
|
||||
/*StaticLoc=*/SourceLoc(),
|
||||
StaticSpellingKind::None, // TODO: we should handle static vars.
|
||||
/*Async=*/false, /*AsyncLoc=*/SourceLoc(),
|
||||
/*Throws=*/false,
|
||||
/*ThrowsLoc=*/SourceLoc(),
|
||||
/*GenericParams=*/nullptr, ParameterList::createEmpty(ctx), computedType,
|
||||
declContext);
|
||||
getterDecl->setIsTransparent(true);
|
||||
getterDecl->setAccess(AccessLevel::Public);
|
||||
getterDecl->setBodySynthesizer(synthesizeBaseClassFieldGetterBody,
|
||||
baseClassVar);
|
||||
|
||||
auto newValueParam =
|
||||
new (ctx) ParamDecl(SourceLoc(), SourceLoc(), Identifier(), SourceLoc(),
|
||||
ctx.getIdentifier("newValue"), declContext);
|
||||
newValueParam->setSpecifier(ParamSpecifier::Default);
|
||||
newValueParam->setInterfaceType(computedType);
|
||||
auto setterDecl = AccessorDecl::create(
|
||||
ctx,
|
||||
/*FuncLoc=*/SourceLoc(),
|
||||
/*AccessorKeywordLoc=*/SourceLoc(), AccessorKind::Set, computedVar,
|
||||
/*StaticLoc=*/SourceLoc(),
|
||||
StaticSpellingKind::None, // TODO: we should handle static vars.
|
||||
/*Async=*/false, /*AsyncLoc=*/SourceLoc(),
|
||||
/*Throws=*/false,
|
||||
/*ThrowsLoc=*/SourceLoc(),
|
||||
/*GenericParams=*/nullptr, ParameterList::create(ctx, {newValueParam}),
|
||||
TupleType::getEmpty(ctx), declContext);
|
||||
setterDecl->setIsTransparent(true);
|
||||
setterDecl->setAccess(AccessLevel::Public);
|
||||
setterDecl->setBodySynthesizer(synthesizeBaseClassFieldSetterBody,
|
||||
baseClassVar);
|
||||
setterDecl->setSelfAccessKind(SelfAccessKind::Mutating);
|
||||
|
||||
return {getterDecl, setterDecl};
|
||||
}
|
||||
|
||||
ValueDecl *cloneBaseMemberDecl(ValueDecl *decl, DeclContext *newContext) {
|
||||
if (auto fn = dyn_cast<FuncDecl>(decl)) {
|
||||
// TODO: function templates are specialized during type checking so to
|
||||
// support these we need to tell Swift to type check the synthesized bodies.
|
||||
// TODO: we also currently don't support static functions. That shouldn't be
|
||||
// too hard.
|
||||
if (fn->isStatic() ||
|
||||
(fn->getClangDecl() &&
|
||||
isa<clang::FunctionTemplateDecl>(fn->getClangDecl())))
|
||||
return nullptr;
|
||||
|
||||
auto out = FuncDecl::createImplicit(
|
||||
fn->getASTContext(), fn->getStaticSpelling(), fn->getName(),
|
||||
fn->getNameLoc(), fn->hasAsync(), fn->hasThrows(),
|
||||
fn->getGenericParams(), fn->getParameters(),
|
||||
fn->getResultInterfaceType(), newContext);
|
||||
out->copyFormalAccessFrom(fn);
|
||||
out->setBodySynthesizer(synthesizeBaseClassMethodBody, fn);
|
||||
out->setSelfAccessKind(fn->getSelfAccessKind());
|
||||
return out;
|
||||
}
|
||||
|
||||
if (auto var = dyn_cast<VarDecl>(decl)) {
|
||||
auto rawMemory = allocateMemoryForDecl<VarDecl>(var->getASTContext(),
|
||||
sizeof(VarDecl), false);
|
||||
auto out =
|
||||
new (rawMemory) VarDecl(var->isStatic(), var->getIntroducer(),
|
||||
var->getLoc(), var->getName(), newContext);
|
||||
out->setInterfaceType(var->getInterfaceType());
|
||||
out->setIsObjC(var->isObjC());
|
||||
out->setIsDynamic(var->isDynamic());
|
||||
out->copyFormalAccessFrom(var);
|
||||
out->setAccessors(SourceLoc(),
|
||||
makeBaseClassFieldAccessors(newContext, out, var),
|
||||
SourceLoc());
|
||||
out->setImplInfo(StorageImplInfo::getComputed(StorageIsMutable));
|
||||
out->setIsSetterMutating(true);
|
||||
return out;
|
||||
}
|
||||
|
||||
if (auto typeAlias = dyn_cast<TypeAliasDecl>(decl)) {
|
||||
auto rawMemory = allocateMemoryForDecl<TypeAliasDecl>(
|
||||
typeAlias->getASTContext(), sizeof(TypeAliasDecl), false);
|
||||
auto out = new (rawMemory) TypeAliasDecl(
|
||||
typeAlias->getLoc(), typeAlias->getEqualLoc(), typeAlias->getName(),
|
||||
typeAlias->getNameLoc(), typeAlias->getGenericParams(), newContext);
|
||||
out->setUnderlyingType(typeAlias->getUnderlyingType());
|
||||
out->copyFormalAccessFrom(typeAlias);
|
||||
return out;
|
||||
}
|
||||
|
||||
if (auto typeDecl = dyn_cast<TypeDecl>(decl)) {
|
||||
auto rawMemory = allocateMemoryForDecl<TypeAliasDecl>(
|
||||
typeDecl->getASTContext(), sizeof(TypeAliasDecl), false);
|
||||
auto out = new (rawMemory) TypeAliasDecl(
|
||||
typeDecl->getLoc(), typeDecl->getLoc(), typeDecl->getName(),
|
||||
typeDecl->getLoc(), nullptr, newContext);
|
||||
out->setUnderlyingType(typeDecl->getInterfaceType());
|
||||
out->copyFormalAccessFrom(typeDecl);
|
||||
return out;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
TinyPtrVector<ValueDecl *> ClangRecordMemberLookup::evaluate(
|
||||
Evaluator &evaluator, ClangRecordMemberLookupDescriptor desc) const {
|
||||
NominalTypeDecl *recordDecl = desc.recordDecl;
|
||||
@@ -4372,6 +4783,34 @@ TinyPtrVector<ValueDecl *> ClangRecordMemberLookup::evaluate(
|
||||
}
|
||||
}
|
||||
|
||||
// If this is a C++ record, look through any base classes.
|
||||
if (auto cxxRecord =
|
||||
dyn_cast<clang::CXXRecordDecl>(recordDecl->getClangDecl())) {
|
||||
for (auto base : cxxRecord->bases()) {
|
||||
clang::QualType baseType = base.getType();
|
||||
if (auto spectType = dyn_cast<clang::TemplateSpecializationType>(baseType))
|
||||
baseType = spectType->desugar();
|
||||
if (!isa<clang::RecordType>(baseType.getCanonicalType()))
|
||||
continue;
|
||||
|
||||
auto *baseRecord = baseType->getAs<clang::RecordType>()->getDecl();
|
||||
if (auto import =
|
||||
ctx.getClangModuleLoader()->importDeclDirectly(baseRecord)) {
|
||||
// If we are looking up the base class, go no further. We will have
|
||||
// already found it during the other lookup.
|
||||
if (cast<ValueDecl>(import)->getName() == name)
|
||||
continue;
|
||||
|
||||
auto baseResults = cast<StructDecl>(import)->lookupDirect(name);
|
||||
for (auto foundInBase : baseResults) {
|
||||
if (auto newDecl = cloneBaseMemberDecl(foundInBase, recordDecl)) {
|
||||
result.push_back(newDecl);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -692,7 +692,7 @@ static AccessorDecl *makeStructRawValueGetter(
|
||||
assert(storedVar->hasStorage());
|
||||
|
||||
ASTContext &C = Impl.SwiftContext;
|
||||
|
||||
|
||||
auto *params = ParameterList::createEmpty(C);
|
||||
|
||||
auto computedType = computedVar->getInterfaceType();
|
||||
@@ -726,7 +726,7 @@ static AccessorDecl *makeFieldGetterDecl(ClangImporter::Implementation &Impl,
|
||||
auto &C = Impl.SwiftContext;
|
||||
|
||||
auto *params = ParameterList::createEmpty(C);
|
||||
|
||||
|
||||
auto getterType = importedFieldDecl->getInterfaceType();
|
||||
auto getterDecl = AccessorDecl::create(C,
|
||||
/*FuncLoc=*/importedFieldDecl->getLoc(),
|
||||
@@ -1152,7 +1152,7 @@ makeBitFieldAccessors(ClangImporter::Implementation &Impl,
|
||||
cSetterParamTypes,
|
||||
clang::FunctionProtoType::ExtProtoInfo());
|
||||
auto cSetterTypeInfo = Ctx.getTrivialTypeSourceInfo(cSetterType);
|
||||
|
||||
|
||||
auto cSetterDecl = clang::FunctionDecl::Create(Ctx,
|
||||
structDecl->getDeclContext(),
|
||||
clang::SourceLocation(),
|
||||
@@ -1184,7 +1184,7 @@ makeBitFieldAccessors(ClangImporter::Implementation &Impl,
|
||||
clang::SC_None,
|
||||
nullptr);
|
||||
cGetterDecl->setParams(cGetterSelf);
|
||||
|
||||
|
||||
auto cGetterSelfExpr = new (Ctx) clang::DeclRefExpr(Ctx, cGetterSelf, false,
|
||||
recordType,
|
||||
clang::VK_PRValue,
|
||||
@@ -1197,7 +1197,7 @@ makeBitFieldAccessors(ClangImporter::Implementation &Impl,
|
||||
clang::VK_PRValue,
|
||||
clang::OK_BitField);
|
||||
|
||||
|
||||
|
||||
auto cGetterBody = clang::ReturnStmt::Create(Ctx, clang::SourceLocation(),
|
||||
cGetterExpr,
|
||||
nullptr);
|
||||
@@ -1228,12 +1228,12 @@ makeBitFieldAccessors(ClangImporter::Implementation &Impl,
|
||||
nullptr);
|
||||
cSetterParams.push_back(cSetterSelf);
|
||||
cSetterDecl->setParams(cSetterParams);
|
||||
|
||||
|
||||
auto cSetterSelfExpr = new (Ctx) clang::DeclRefExpr(Ctx, cSetterSelf, false,
|
||||
recordPointerType,
|
||||
clang::VK_PRValue,
|
||||
clang::SourceLocation());
|
||||
|
||||
|
||||
auto cSetterMemberExpr = clang::MemberExpr::CreateImplicit(Ctx,
|
||||
cSetterSelfExpr,
|
||||
/*isarrow=*/true,
|
||||
@@ -1241,7 +1241,7 @@ makeBitFieldAccessors(ClangImporter::Implementation &Impl,
|
||||
fieldType,
|
||||
clang::VK_LValue,
|
||||
clang::OK_BitField);
|
||||
|
||||
|
||||
auto cSetterValueExpr = new (Ctx) clang::DeclRefExpr(Ctx, cSetterValue, false,
|
||||
fieldType,
|
||||
clang::VK_PRValue,
|
||||
@@ -1256,7 +1256,7 @@ makeBitFieldAccessors(ClangImporter::Implementation &Impl,
|
||||
clang::OK_Ordinary,
|
||||
clang::SourceLocation(),
|
||||
clang::FPOptionsOverride());
|
||||
|
||||
|
||||
cSetterDecl->setBody(cSetterExpr);
|
||||
}
|
||||
|
||||
@@ -2419,7 +2419,7 @@ namespace {
|
||||
if (auto *typedefForAnon = decl->getTypedefNameForAnonDecl())
|
||||
return importFullName(typedefForAnon);
|
||||
}
|
||||
|
||||
|
||||
return {ImportedName(), None};
|
||||
}
|
||||
|
||||
@@ -2806,7 +2806,7 @@ namespace {
|
||||
});
|
||||
|
||||
Result->setUnderlyingType(SwiftType);
|
||||
|
||||
|
||||
// Make Objective-C's 'id' unavailable.
|
||||
if (Impl.SwiftContext.LangOpts.EnableObjCInterop && isObjCId(Decl)) {
|
||||
auto attr = AvailableAttr::createPlatformAgnostic(
|
||||
@@ -2882,7 +2882,7 @@ namespace {
|
||||
Impl.importDeclContextOf(decl, importedName.getEffectiveContext());
|
||||
if (!dc)
|
||||
return nullptr;
|
||||
|
||||
|
||||
auto name = importedName.getDeclName().getBaseIdentifier();
|
||||
|
||||
// Create the enum declaration and record it.
|
||||
@@ -2948,7 +2948,7 @@ namespace {
|
||||
ProtocolDecl *bridgedNSError = nullptr;
|
||||
ClassDecl *nsErrorDecl = nullptr;
|
||||
ProtocolDecl *errorCodeProto = nullptr;
|
||||
if (enumInfo.isErrorEnum() &&
|
||||
if (enumInfo.isErrorEnum() &&
|
||||
(bridgedNSError =
|
||||
C.getProtocol(KnownProtocolKind::BridgedStoredNSError)) &&
|
||||
(nsErrorDecl = C.getNSErrorDecl()) &&
|
||||
@@ -3124,7 +3124,7 @@ namespace {
|
||||
Impl.ImportedDecls[{canonicalClangDecl, getVersion()}] = result;
|
||||
|
||||
// Import each of the enumerators.
|
||||
|
||||
|
||||
bool addEnumeratorsAsMembers;
|
||||
switch (enumKind) {
|
||||
case EnumKind::Constants:
|
||||
@@ -3282,7 +3282,7 @@ namespace {
|
||||
addDecl(result, enumeratorDecl);
|
||||
for (auto *variant : variantDecls)
|
||||
addDecl(result, variant);
|
||||
|
||||
|
||||
// If there is an error wrapper, add an alias within the
|
||||
// wrapper to the corresponding value within the enumerator
|
||||
// context.
|
||||
@@ -3334,7 +3334,7 @@ namespace {
|
||||
// Track whether this record contains fields we can't reference in Swift
|
||||
// as stored properties.
|
||||
bool hasUnreferenceableStorage = false;
|
||||
|
||||
|
||||
// Track whether this record contains fields that can't be zero-
|
||||
// initialized.
|
||||
bool hasZeroInitializableStorage = true;
|
||||
@@ -3576,6 +3576,12 @@ namespace {
|
||||
|
||||
const clang::CXXRecordDecl *cxxRecordDecl =
|
||||
dyn_cast<clang::CXXRecordDecl>(decl);
|
||||
bool hasBaseClasses = cxxRecordDecl && !cxxRecordDecl->bases().empty();
|
||||
if (hasBaseClasses) {
|
||||
hasUnreferenceableStorage = true;
|
||||
hasMemberwiseInitializer = false;
|
||||
}
|
||||
|
||||
if (hasZeroInitializableStorage && !cxxRecordDecl) {
|
||||
// Add default constructor for the struct if compiling in C mode.
|
||||
// If we're compiling for C++, we'll import the C++ default constructor
|
||||
@@ -4212,10 +4218,10 @@ namespace {
|
||||
DeclName ctorName(Impl.SwiftContext, DeclBaseName::createConstructor(),
|
||||
bodyParams);
|
||||
result = Impl.createDeclWithClangNode<ConstructorDecl>(
|
||||
clangNode, AccessLevel::Public, ctorName, loc,
|
||||
clangNode, AccessLevel::Public, ctorName, loc,
|
||||
/*failable=*/false, /*FailabilityLoc=*/SourceLoc(),
|
||||
/*Async=*/false, /*AsyncLoc=*/SourceLoc(),
|
||||
/*Throws=*/false, /*ThrowsLoc=*/SourceLoc(),
|
||||
/*Throws=*/false, /*ThrowsLoc=*/SourceLoc(),
|
||||
bodyParams, genericParams, dc);
|
||||
} else {
|
||||
auto resultTy = importedType.getType();
|
||||
@@ -4592,25 +4598,25 @@ namespace {
|
||||
// Only import types for now.
|
||||
if (!isa<clang::TypeDecl>(decl->getUnderlyingDecl()))
|
||||
return nullptr;
|
||||
|
||||
|
||||
ImportedName importedName;
|
||||
Optional<ImportedName> correctSwiftName;
|
||||
std::tie(importedName, correctSwiftName) = importFullName(decl);
|
||||
auto Name = importedName.getDeclName().getBaseIdentifier();
|
||||
if (Name.empty())
|
||||
return nullptr;
|
||||
|
||||
|
||||
// If we've been asked to produce a compatibility stub, handle it via a
|
||||
// typealias.
|
||||
if (correctSwiftName)
|
||||
return importCompatibilityTypeAlias(decl, importedName,
|
||||
*correctSwiftName);
|
||||
|
||||
|
||||
auto DC =
|
||||
Impl.importDeclContextOf(decl, importedName.getEffectiveContext());
|
||||
if (!DC)
|
||||
return nullptr;
|
||||
|
||||
|
||||
Decl *SwiftDecl = Impl.importDecl(decl->getUnderlyingDecl(), getActiveSwiftVersion());
|
||||
if (!SwiftDecl)
|
||||
return nullptr;
|
||||
@@ -4618,7 +4624,7 @@ namespace {
|
||||
const TypeDecl *SwiftTypeDecl = dyn_cast<TypeDecl>(SwiftDecl);
|
||||
if (!SwiftTypeDecl)
|
||||
return nullptr;
|
||||
|
||||
|
||||
auto Loc = Impl.importSourceLoc(decl->getLocation());
|
||||
auto Result = Impl.createDeclWithClangNode<TypeAliasDecl>(
|
||||
decl,
|
||||
@@ -5286,7 +5292,7 @@ namespace {
|
||||
objcClass->getDeclaredType());
|
||||
Impl.SwiftContext.evaluator.cacheOutput(ExtendedNominalRequest{result},
|
||||
std::move(objcClass));
|
||||
|
||||
|
||||
// Determine the type and generic args of the extension.
|
||||
if (objcClass->getGenericParams()) {
|
||||
result->setGenericSignature(objcClass->getGenericSignature());
|
||||
@@ -5305,7 +5311,7 @@ namespace {
|
||||
}
|
||||
|
||||
template <typename T, typename U>
|
||||
T *resolveSwiftDeclImpl(const U *decl, Identifier name,
|
||||
T *resolveSwiftDeclImpl(const U *decl, Identifier name,
|
||||
bool hasKnownSwiftName, ModuleDecl *overlay) {
|
||||
const auto &languageVersion =
|
||||
Impl.SwiftContext.LangOpts.EffectiveLanguageVersion;
|
||||
@@ -6178,7 +6184,7 @@ Decl *SwiftDeclConverter::importCompatibilityTypeAlias(
|
||||
}
|
||||
|
||||
alias->setUnderlyingType(typeDecl->getDeclaredInterfaceType());
|
||||
|
||||
|
||||
// Record that this is the official version of this declaration.
|
||||
Impl.ImportedDecls[{decl->getCanonicalDecl(), getVersion()}] = alias;
|
||||
markAsVariant(alias, correctSwiftName);
|
||||
@@ -9335,7 +9341,7 @@ ClangImporter::Implementation::importMirroredDecl(const clang::NamedDecl *decl,
|
||||
|
||||
auto updateMirroredDecl = [&](Decl *result) {
|
||||
result->setImplicit();
|
||||
|
||||
|
||||
// Map the Clang attributes onto Swift attributes.
|
||||
importAttributes(decl, result);
|
||||
|
||||
@@ -9926,10 +9932,10 @@ static void loadAllMembersOfSuperclassIfNeeded(ClassDecl *CD) {
|
||||
E->loadAllMembers();
|
||||
}
|
||||
|
||||
void ClangImporter::Implementation::loadAllMembersOfRecordDecl(
|
||||
NominalTypeDecl *recordDecl) {
|
||||
auto clangRecord = cast<clang::RecordDecl>(recordDecl->getClangDecl());
|
||||
ValueDecl *cloneBaseMemberDecl(ValueDecl *decl, DeclContext *newContext);
|
||||
|
||||
void ClangImporter::Implementation::loadAllMembersOfRecordDecl(
|
||||
NominalTypeDecl *swiftDecl, const clang::RecordDecl *clangRecord) {
|
||||
// Import all of the members.
|
||||
llvm::SmallVector<Decl *, 16> members;
|
||||
for (const clang::Decl *m : clangRecord->decls()) {
|
||||
@@ -9955,12 +9961,36 @@ void ClangImporter::Implementation::loadAllMembersOfRecordDecl(
|
||||
|
||||
// Add the members here.
|
||||
for (auto member: members) {
|
||||
// This means we found a member in a C++ record's base class.
|
||||
if (swiftDecl->getClangDecl() != clangRecord) {
|
||||
// So we need to clone the member into the derived class.
|
||||
if (auto newDecl = cloneBaseMemberDecl(cast<ValueDecl>(member), swiftDecl)) {
|
||||
swiftDecl->addMember(newDecl);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// FIXME: constructors are added eagerly, but shouldn't be
|
||||
// FIXME: subscripts are added eagerly, but shouldn't be
|
||||
if (!isa<AccessorDecl>(member) &&
|
||||
!isa<SubscriptDecl>(member) &&
|
||||
!isa<ConstructorDecl>(member))
|
||||
recordDecl->addMember(member);
|
||||
!isa<ConstructorDecl>(member)) {
|
||||
swiftDecl->addMember(member);
|
||||
}
|
||||
}
|
||||
|
||||
// If this is a C++ record, look through the base classes too.
|
||||
if (auto cxxRecord = dyn_cast<clang::CXXRecordDecl>(clangRecord)) {
|
||||
for (auto base : cxxRecord->bases()) {
|
||||
clang::QualType baseType = base.getType();
|
||||
if (auto spectType = dyn_cast<clang::TemplateSpecializationType>(baseType))
|
||||
baseType = spectType->desugar();
|
||||
if (!isa<clang::RecordType>(baseType))
|
||||
continue;
|
||||
|
||||
auto *baseRecord = cast<clang::RecordType>(baseType)->getDecl();
|
||||
loadAllMembersOfRecordDecl(swiftDecl, baseRecord);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9996,7 +10026,8 @@ ClangImporter::Implementation::loadAllMembers(Decl *D, uint64_t extra) {
|
||||
}
|
||||
|
||||
if (isa_and_nonnull<clang::RecordDecl>(D->getClangDecl())) {
|
||||
loadAllMembersOfRecordDecl(cast<NominalTypeDecl>(D));
|
||||
loadAllMembersOfRecordDecl(cast<NominalTypeDecl>(D),
|
||||
cast<clang::RecordDecl>(D->getClangDecl()));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -1460,7 +1460,8 @@ private:
|
||||
void
|
||||
loadAllMembersOfObjcContainer(Decl *D,
|
||||
const clang::ObjCContainerDecl *objcContainer);
|
||||
void loadAllMembersOfRecordDecl(NominalTypeDecl *recordDecl);
|
||||
void loadAllMembersOfRecordDecl(NominalTypeDecl *swiftDecl,
|
||||
const clang::RecordDecl *clangRecord);
|
||||
|
||||
void collectMembersToAdd(const clang::ObjCContainerDecl *objcContainer,
|
||||
Decl *D, DeclContext *DC,
|
||||
|
||||
@@ -96,7 +96,7 @@ namespace {
|
||||
StringRef getFieldName() const {
|
||||
return Field->getName().str();
|
||||
}
|
||||
|
||||
|
||||
SILType getType(IRGenModule &IGM, SILType T) const {
|
||||
return T.getFieldType(Field, IGM.getSILModule(),
|
||||
IGM.getMaximalTypeExpansionContext());
|
||||
@@ -117,7 +117,7 @@ namespace {
|
||||
: RecordField(layout, explosionBegin, explosionEnd),
|
||||
Field(swiftField) {}
|
||||
|
||||
VarDecl * const Field;
|
||||
VarDecl *Field;
|
||||
|
||||
StringRef getFieldName() const {
|
||||
if (Field) return Field->getName().str();
|
||||
@@ -160,36 +160,31 @@ namespace {
|
||||
}
|
||||
|
||||
/// Given a full struct explosion, project out a single field.
|
||||
void projectFieldFromExplosion(IRGenFunction &IGF,
|
||||
Explosion &in,
|
||||
VarDecl *field,
|
||||
Explosion &out) const {
|
||||
virtual void projectFieldFromExplosion(IRGenFunction &IGF, Explosion &in,
|
||||
VarDecl *field,
|
||||
Explosion &out) const {
|
||||
auto &fieldInfo = getFieldInfo(field);
|
||||
|
||||
// If the field requires no storage, there's nothing to do.
|
||||
if (fieldInfo.isEmpty())
|
||||
return;
|
||||
|
||||
|
||||
// Otherwise, project from the base.
|
||||
auto fieldRange = fieldInfo.getProjectionRange();
|
||||
auto elements = in.getRange(fieldRange.first, fieldRange.second);
|
||||
out.add(elements);
|
||||
}
|
||||
|
||||
|
||||
/// Given the address of a struct value, project out the address of a
|
||||
/// single field.
|
||||
Address projectFieldAddress(IRGenFunction &IGF,
|
||||
Address addr,
|
||||
SILType T,
|
||||
Address projectFieldAddress(IRGenFunction &IGF, Address addr, SILType T,
|
||||
const FieldInfoType &field) const {
|
||||
return asImpl().projectFieldAddress(IGF, addr, T, field.Field);
|
||||
}
|
||||
|
||||
/// Given the address of a struct value, project out the address of a
|
||||
/// single field.
|
||||
Address projectFieldAddress(IRGenFunction &IGF,
|
||||
Address addr,
|
||||
SILType T,
|
||||
Address projectFieldAddress(IRGenFunction &IGF, Address addr, SILType T,
|
||||
VarDecl *field) const {
|
||||
auto &fieldInfo = getFieldInfo(field);
|
||||
if (fieldInfo.isEmpty())
|
||||
@@ -255,7 +250,7 @@ namespace {
|
||||
return false;
|
||||
return fields[0].getTypeInfo().isSingleRetainablePointer(expansion, rc);
|
||||
}
|
||||
|
||||
|
||||
void verify(IRGenTypeVerifierFunction &IGF,
|
||||
llvm::Value *metadata,
|
||||
SILType structType) const override {
|
||||
@@ -289,14 +284,14 @@ namespace {
|
||||
FindOffsetOfFieldOffsetVector>::addFieldOffset(Field);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
FindOffsetOfFieldOffsetVector scanner(IGF.IGM, field.Field);
|
||||
scanner.layout();
|
||||
|
||||
|
||||
if (scanner.FieldOffset == Size::invalid()
|
||||
|| scanner.AddressPoint == Size::invalid())
|
||||
continue;
|
||||
|
||||
|
||||
// Load the offset from the field offset vector and ensure it matches
|
||||
// the compiler's idea of the offset.
|
||||
auto metadataBytes =
|
||||
@@ -314,7 +309,7 @@ namespace {
|
||||
IGF.Builder.CreateLoad(fieldOffsetPtr, Alignment(4));
|
||||
fieldOffset = IGF.Builder.CreateZExtOrBitCast(fieldOffset,
|
||||
IGF.IGM.SizeTy);
|
||||
|
||||
|
||||
IGF.verifyValues(metadata, fieldOffset,
|
||||
IGF.IGM.getSize(field.getFixedByteOffset()),
|
||||
Twine("offset of struct field ") + field.getFieldName());
|
||||
@@ -334,21 +329,70 @@ namespace {
|
||||
class LoadableClangRecordTypeInfo final :
|
||||
public StructTypeInfoBase<LoadableClangRecordTypeInfo, LoadableTypeInfo,
|
||||
ClangFieldInfo> {
|
||||
IRGenModule &IGM;
|
||||
const clang::RecordDecl *ClangDecl;
|
||||
|
||||
template <class Fn>
|
||||
void forEachNonEmptyBase(Fn fn) const {
|
||||
auto &layout = ClangDecl->getASTContext().getASTRecordLayout(ClangDecl);
|
||||
|
||||
if (auto cxxRecord = dyn_cast<clang::CXXRecordDecl>(ClangDecl)) {
|
||||
for (auto base : cxxRecord->bases()) {
|
||||
auto baseRecord = cast<clang::RecordType>(base.getType())->getDecl();
|
||||
auto baseCxxRecord = cast<clang::CXXRecordDecl>(baseRecord);
|
||||
|
||||
if (baseCxxRecord->isEmpty())
|
||||
continue;
|
||||
|
||||
auto offset = layout.getBaseClassOffset(baseCxxRecord);
|
||||
auto size =
|
||||
ClangDecl->getASTContext().getTypeSizeInChars(base.getType());
|
||||
fn(base.getType(), offset, size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <class Fn>
|
||||
void forEachNonEmptyBaseTypeInfo(Fn fn) const {
|
||||
forEachNonEmptyBase([&](clang::QualType, clang::CharUnits,
|
||||
clang::CharUnits size) {
|
||||
auto &typeInfo = IGM.getOpaqueStorageTypeInfo(Size(size.getQuantity()),
|
||||
Alignment(1));
|
||||
fn(typeInfo);
|
||||
});
|
||||
}
|
||||
|
||||
template <class Fn>
|
||||
void forEachNonEmptyBaseTypeInfoAndBaseAddress(IRGenFunction &IGF,
|
||||
Address addr, Fn fn) const {
|
||||
forEachNonEmptyBase([&](clang::QualType,
|
||||
clang::CharUnits offset, clang::CharUnits size) {
|
||||
auto &typeInfo = IGM.getOpaqueStorageTypeInfo(Size(size.getQuantity()),
|
||||
Alignment(1));
|
||||
|
||||
Address baseAddr = addr;
|
||||
if (offset.getQuantity() != 0) {
|
||||
auto baseAddrVal =
|
||||
IGF.Builder.CreateBitCast(addr.getAddress(), IGF.IGM.Int8PtrTy);
|
||||
baseAddrVal =
|
||||
IGF.Builder.CreateConstGEP1_64(baseAddrVal, offset.getQuantity());
|
||||
baseAddr = Address(baseAddrVal, Alignment(1));
|
||||
}
|
||||
|
||||
fn(typeInfo, baseAddr);
|
||||
});
|
||||
}
|
||||
|
||||
public:
|
||||
LoadableClangRecordTypeInfo(ArrayRef<ClangFieldInfo> fields,
|
||||
unsigned explosionSize,
|
||||
llvm::Type *storageType, Size size,
|
||||
SpareBitVector &&spareBits, Alignment align,
|
||||
const clang::RecordDecl *clangDecl)
|
||||
: StructTypeInfoBase(StructTypeInfoKind::LoadableClangRecordTypeInfo,
|
||||
fields, explosionSize,
|
||||
storageType, size, std::move(spareBits),
|
||||
align, IsPOD, IsFixedSize),
|
||||
ClangDecl(clangDecl)
|
||||
{
|
||||
}
|
||||
unsigned explosionSize, IRGenModule &IGM,
|
||||
llvm::Type *storageType, Size size,
|
||||
SpareBitVector &&spareBits, Alignment align,
|
||||
const clang::RecordDecl *clangDecl)
|
||||
: StructTypeInfoBase(StructTypeInfoKind::LoadableClangRecordTypeInfo,
|
||||
fields, explosionSize, storageType, size,
|
||||
std::move(spareBits), align, IsPOD, IsFixedSize),
|
||||
IGM(IGM), ClangDecl(clangDecl) {}
|
||||
|
||||
TypeLayoutEntry *buildTypeLayoutEntry(IRGenModule &IGM,
|
||||
SILType T) const override {
|
||||
@@ -383,9 +427,110 @@ namespace {
|
||||
|
||||
void addToAggLowering(IRGenModule &IGM, SwiftAggLowering &lowering,
|
||||
Size offset) const override {
|
||||
auto &layout = ClangDecl->getASTContext().getASTRecordLayout(ClangDecl);
|
||||
|
||||
forEachNonEmptyBase([&](clang::QualType type, clang::CharUnits offset,
|
||||
clang::CharUnits) {
|
||||
lowering.addTypedData(type, offset);
|
||||
});
|
||||
|
||||
lowering.addTypedData(ClangDecl, offset.asCharUnits());
|
||||
}
|
||||
|
||||
void getSchema(ExplosionSchema &schema) const override {
|
||||
forEachNonEmptyBaseTypeInfo([&](const LoadableTypeInfo &typeInfo) {
|
||||
typeInfo.getSchema(schema);
|
||||
});
|
||||
|
||||
for (auto &field : getFields()) {
|
||||
field.getTypeInfo().getSchema(schema);
|
||||
}
|
||||
}
|
||||
|
||||
void projectFieldFromExplosion(IRGenFunction &IGF, Explosion &in,
|
||||
VarDecl *field,
|
||||
Explosion &out) const override {
|
||||
auto &fieldInfo = getFieldInfo(field);
|
||||
|
||||
// If the field requires no storage, there's nothing to do.
|
||||
if (fieldInfo.isEmpty())
|
||||
return;
|
||||
|
||||
unsigned baseOffset = 0;
|
||||
if (auto cxxRecord = dyn_cast<clang::CXXRecordDecl>(ClangDecl)) {
|
||||
baseOffset =
|
||||
std::distance(cxxRecord->bases().begin(), cxxRecord->bases().end());
|
||||
}
|
||||
|
||||
// Otherwise, project from the base.
|
||||
auto fieldRange = fieldInfo.getProjectionRange();
|
||||
auto elements = in.getRange(fieldRange.first + baseOffset,
|
||||
fieldRange.second + baseOffset);
|
||||
out.add(elements);
|
||||
}
|
||||
|
||||
void reexplode(IRGenFunction &IGF, Explosion &src,
|
||||
Explosion &dest) const override {
|
||||
forEachNonEmptyBaseTypeInfo([&](const LoadableTypeInfo &typeInfo) {
|
||||
typeInfo.reexplode(IGF, src, dest);
|
||||
});
|
||||
|
||||
for (auto &field : getFields()) {
|
||||
cast<LoadableTypeInfo>(field.getTypeInfo()).reexplode(IGF, src, dest);
|
||||
}
|
||||
}
|
||||
|
||||
void initialize(IRGenFunction &IGF, Explosion &e, Address addr,
|
||||
bool isOutlined) const override {
|
||||
forEachNonEmptyBaseTypeInfoAndBaseAddress(
|
||||
IGF, addr, [&](const LoadableTypeInfo &typeInfo, Address baseAddr) {
|
||||
typeInfo.initialize(IGF, e, baseAddr, isOutlined);
|
||||
});
|
||||
|
||||
for (auto &field : getFields()) {
|
||||
if (field.isEmpty())
|
||||
continue;
|
||||
|
||||
Address fieldAddr = field.projectAddress(IGF, addr, None);
|
||||
cast<LoadableTypeInfo>(field.getTypeInfo())
|
||||
.initialize(IGF, e, fieldAddr, isOutlined);
|
||||
}
|
||||
}
|
||||
|
||||
void loadAsTake(IRGenFunction &IGF, Address addr,
|
||||
Explosion &e) const override {
|
||||
forEachNonEmptyBaseTypeInfoAndBaseAddress(
|
||||
IGF, addr, [&](const LoadableTypeInfo &typeInfo, Address baseAddr) {
|
||||
typeInfo.loadAsTake(IGF, baseAddr, e);
|
||||
});
|
||||
|
||||
for (auto &field : getFields()) {
|
||||
if (field.isEmpty())
|
||||
continue;
|
||||
|
||||
Address fieldAddr = field.projectAddress(IGF, addr, None);
|
||||
cast<LoadableTypeInfo>(field.getTypeInfo())
|
||||
.loadAsTake(IGF, fieldAddr, e);
|
||||
}
|
||||
}
|
||||
|
||||
void loadAsCopy(IRGenFunction &IGF, Address addr,
|
||||
Explosion &e) const override {
|
||||
forEachNonEmptyBaseTypeInfoAndBaseAddress(
|
||||
IGF, addr, [&](const LoadableTypeInfo &typeInfo, Address baseAddr) {
|
||||
typeInfo.loadAsCopy(IGF, baseAddr, e);
|
||||
});
|
||||
|
||||
for (auto &field : getFields()) {
|
||||
if (field.isEmpty())
|
||||
continue;
|
||||
|
||||
Address fieldAddr = field.projectAddress(IGF, addr, None);
|
||||
cast<LoadableTypeInfo>(field.getTypeInfo())
|
||||
.loadAsCopy(IGF, fieldAddr, e);
|
||||
}
|
||||
}
|
||||
|
||||
llvm::NoneType getNonFixedOffsets(IRGenFunction &IGF) const {
|
||||
return None;
|
||||
}
|
||||
@@ -717,7 +862,7 @@ namespace {
|
||||
llvm_unreachable("non-fixed field in fixed struct?");
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/// Accessor for the non-fixed offsets of a struct type.
|
||||
class StructNonFixedOffsets : public NonFixedOffsetsImpl {
|
||||
SILType TheStruct;
|
||||
@@ -725,7 +870,7 @@ namespace {
|
||||
StructNonFixedOffsets(SILType type) : TheStruct(type) {
|
||||
assert(TheStruct.getStructOrBoundGenericStruct());
|
||||
}
|
||||
|
||||
|
||||
llvm::Value *getOffsetForIndex(IRGenFunction &IGF, unsigned index) override {
|
||||
auto &layout =
|
||||
IGF.IGM.getMetadataLayout(TheStruct.getStructOrBoundGenericStruct());
|
||||
@@ -981,10 +1126,9 @@ public:
|
||||
return AddressOnlyClangRecordTypeInfo::create(
|
||||
FieldInfos, llvmType, TotalStride, TotalAlignment, ClangDecl);
|
||||
}
|
||||
return LoadableClangRecordTypeInfo::create(FieldInfos, NextExplosionIndex,
|
||||
llvmType, TotalStride,
|
||||
std::move(SpareBits), TotalAlignment,
|
||||
ClangDecl);
|
||||
return LoadableClangRecordTypeInfo::create(
|
||||
FieldInfos, NextExplosionIndex, IGM, llvmType, TotalStride,
|
||||
std::move(SpareBits), TotalAlignment, ClangDecl);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
48
test/Interop/Cxx/class/inheritance/Inputs/fields.h
Normal file
48
test/Interop/Cxx/class/inheritance/Inputs/fields.h
Normal file
@@ -0,0 +1,48 @@
|
||||
template <class From, class To>
|
||||
To __swift_interopStaticCast(From from) { return from; }
|
||||
|
||||
struct HasThreeFields {
|
||||
int a = 1;
|
||||
int b = 2;
|
||||
int c = 3;
|
||||
};
|
||||
|
||||
struct DerivedWithOneField : HasThreeFields {
|
||||
int d = 4;
|
||||
};
|
||||
|
||||
struct HasOneField {
|
||||
int e = 5;
|
||||
};
|
||||
|
||||
struct DerivedFromAll : HasOneField, DerivedWithOneField {
|
||||
int f = 6;
|
||||
};
|
||||
|
||||
// Non trivial types:
|
||||
|
||||
struct NonTrivial {
|
||||
NonTrivial() {}
|
||||
~NonTrivial() {}
|
||||
};
|
||||
|
||||
struct NonTrivialHasThreeFields : NonTrivial {
|
||||
int a = 1;
|
||||
int b = 2;
|
||||
int c = 3;
|
||||
};
|
||||
|
||||
struct NonTrivialDerivedWithOneField : NonTrivialHasThreeFields {
|
||||
int d = 4;
|
||||
};
|
||||
|
||||
struct NonTrivialHasOneField {
|
||||
NonTrivialHasOneField() {}
|
||||
~NonTrivialHasOneField() {}
|
||||
|
||||
int e = 5;
|
||||
};
|
||||
|
||||
struct NonTrivialDerivedFromAll : NonTrivialHasOneField, NonTrivialDerivedWithOneField {
|
||||
int f = 6;
|
||||
};
|
||||
51
test/Interop/Cxx/class/inheritance/Inputs/functions.h
Normal file
51
test/Interop/Cxx/class/inheritance/Inputs/functions.h
Normal file
@@ -0,0 +1,51 @@
|
||||
template <class From, class To>
|
||||
To __swift_interopStaticCast(From from) {
|
||||
return static_cast<To>(from);
|
||||
}
|
||||
|
||||
struct NonTrivial {
|
||||
NonTrivial() {}
|
||||
~NonTrivial() {}
|
||||
|
||||
inline const char *inNonTrivial() const { return "NonTrivial::inNonTrivial"; }
|
||||
inline const char *inNonTrivialWithArgs(int a, int b) const {
|
||||
return "NonTrivial::inNonTrivialWithArgs";
|
||||
}
|
||||
};
|
||||
|
||||
struct Base {
|
||||
inline const char *mutatingInBase() { return "Base::mutatingInBase"; }
|
||||
inline const char *constInBase() const { return "Base::constInBase"; }
|
||||
// TODO: if these are unnamed we hit an (unrelated) SILGen bug. Same for
|
||||
// subscripts.
|
||||
inline const char *takesArgsInBase(int a, int b, int c) const {
|
||||
return "Base::takesArgsInBase";
|
||||
}
|
||||
|
||||
inline const char *takesNonTrivialInBase(NonTrivial a) const {
|
||||
return "Base::takesNonTrivialInBase";
|
||||
}
|
||||
inline NonTrivial returnsNonTrivialInBase() const { return NonTrivial{}; }
|
||||
|
||||
template <class T>
|
||||
inline const char *templateInBase(T t) const {
|
||||
return "Base::templateInBase";
|
||||
}
|
||||
|
||||
static const char *staticInBase() { return "Base::staticInBase"; }
|
||||
};
|
||||
|
||||
struct OtherBase {
|
||||
inline const char *inOtherBase() const { return "OtherBase::inOtherBase"; }
|
||||
// TODO: test private access
|
||||
};
|
||||
|
||||
struct Derived : Base, OtherBase {
|
||||
inline const char *inDerived() const { return "Derived::inDerived"; }
|
||||
};
|
||||
|
||||
struct DerivedFromDerived : Derived {
|
||||
inline const char *topLevel() const { return "DerivedFromDerived::topLevel"; }
|
||||
};
|
||||
|
||||
struct DerivedFromNonTrivial : NonTrivial {};
|
||||
15
test/Interop/Cxx/class/inheritance/Inputs/module.modulemap
Normal file
15
test/Interop/Cxx/class/inheritance/Inputs/module.modulemap
Normal file
@@ -0,0 +1,15 @@
|
||||
module Functions {
|
||||
header "functions.h"
|
||||
}
|
||||
|
||||
module Fields {
|
||||
header "fields.h"
|
||||
}
|
||||
|
||||
module SubTypes {
|
||||
header "sub-types.h"
|
||||
}
|
||||
|
||||
module TypeAliases {
|
||||
header "type-aliases.h"
|
||||
}
|
||||
27
test/Interop/Cxx/class/inheritance/Inputs/sub-types.h
Normal file
27
test/Interop/Cxx/class/inheritance/Inputs/sub-types.h
Normal file
@@ -0,0 +1,27 @@
|
||||
template <class From, class To>
|
||||
To __swift_interopStaticCast(From from) {
|
||||
return static_cast<To>(from);
|
||||
}
|
||||
|
||||
struct Base {
|
||||
enum class EnumClass : char { eca = 2, ecb = 3, ecc = 4 };
|
||||
enum Enum { ea, eb, ec };
|
||||
|
||||
struct Struct {
|
||||
int sa;
|
||||
int sb;
|
||||
};
|
||||
|
||||
struct Parent {
|
||||
struct Child {
|
||||
int pca;
|
||||
};
|
||||
};
|
||||
|
||||
union Union {
|
||||
int ua;
|
||||
Struct ub;
|
||||
};
|
||||
};
|
||||
|
||||
struct Derived : Base {};
|
||||
13
test/Interop/Cxx/class/inheritance/Inputs/type-aliases.h
Normal file
13
test/Interop/Cxx/class/inheritance/Inputs/type-aliases.h
Normal file
@@ -0,0 +1,13 @@
|
||||
template <class From, class To>
|
||||
To __swift_interopStaticCast(From from) {
|
||||
return static_cast<To>(from);
|
||||
}
|
||||
|
||||
struct Base {
|
||||
struct Struct {};
|
||||
|
||||
using T = int;
|
||||
using U = Struct;
|
||||
};
|
||||
|
||||
struct Derived : Base {};
|
||||
@@ -0,0 +1,67 @@
|
||||
// RUN: %target-swift-ide-test -print-module -module-to-print=Fields -I %S/Inputs -source-filename=x -enable-cxx-interop | %FileCheck %s
|
||||
|
||||
// CHECK: struct HasThreeFields {
|
||||
// CHECK-NEXT: init()
|
||||
// CHECK-NEXT: init(a: Int32, b: Int32, c: Int32)
|
||||
// CHECK-NEXT: var a: Int32
|
||||
// CHECK-NEXT: var b: Int32
|
||||
// CHECK-NEXT: var c: Int32
|
||||
// CHECK-NEXT: }
|
||||
|
||||
// CHECK-NEXT: struct DerivedWithOneField {
|
||||
// CHECK-NEXT: init()
|
||||
// CHECK-NEXT: var d: Int32
|
||||
// CHECK-NEXT: var a: Int32
|
||||
// CHECK-NEXT: var b: Int32
|
||||
// CHECK-NEXT: var c: Int32
|
||||
// CHECK-NEXT: }
|
||||
|
||||
// CHECK-NEXT: struct HasOneField {
|
||||
// CHECK-NEXT: init()
|
||||
// CHECK-NEXT: init(e: Int32)
|
||||
// CHECK-NEXT: var e: Int32
|
||||
// CHECK-NEXT: }
|
||||
|
||||
// CHECK-NEXT: struct DerivedFromAll {
|
||||
// CHECK-NEXT: init()
|
||||
// CHECK-NEXT: var f: Int32
|
||||
// CHECK-NEXT: var e: Int32
|
||||
// CHECK-NEXT: var d: Int32
|
||||
// CHECK-NEXT: var a: Int32
|
||||
// CHECK-NEXT: var b: Int32
|
||||
// CHECK-NEXT: var c: Int32
|
||||
// CHECK-NEXT: }
|
||||
|
||||
// CHECK-NEXT: struct NonTrivial {
|
||||
// CHECK-NEXT: init()
|
||||
// CHECK-NEXT: }
|
||||
|
||||
// CHECK-NEXT: struct NonTrivialHasThreeFields {
|
||||
// CHECK-NEXT: init()
|
||||
// CHECK-NEXT: var a: Int32
|
||||
// CHECK-NEXT: var b: Int32
|
||||
// CHECK-NEXT: var c: Int32
|
||||
// CHECK-NEXT: }
|
||||
|
||||
// CHECK-NEXT: struct NonTrivialDerivedWithOneField {
|
||||
// CHECK-NEXT: init()
|
||||
// CHECK-NEXT: var d: Int32
|
||||
// CHECK-NEXT: var a: Int32
|
||||
// CHECK-NEXT: var b: Int32
|
||||
// CHECK-NEXT: var c: Int32
|
||||
// CHECK-NEXT: }
|
||||
|
||||
// CHECK-NEXT: struct NonTrivialHasOneField {
|
||||
// CHECK-NEXT: init()
|
||||
// CHECK-NEXT: var e: Int32
|
||||
// CHECK-NEXT: }
|
||||
|
||||
// CHECK-NEXT: struct NonTrivialDerivedFromAll {
|
||||
// CHECK-NEXT: init()
|
||||
// CHECK-NEXT: var f: Int32
|
||||
// CHECK-NEXT: var e: Int32
|
||||
// CHECK-NEXT: var d: Int32
|
||||
// CHECK-NEXT: var a: Int32
|
||||
// CHECK-NEXT: var b: Int32
|
||||
// CHECK-NEXT: var c: Int32
|
||||
// CHECK-NEXT: }
|
||||
55
test/Interop/Cxx/class/inheritance/fields.swift
Normal file
55
test/Interop/Cxx/class/inheritance/fields.swift
Normal file
@@ -0,0 +1,55 @@
|
||||
// RUN: %target-run-simple-swift(-I %S/Inputs/ -Xfrontend -enable-cxx-interop)
|
||||
//
|
||||
// REQUIRES: executable_test
|
||||
//
|
||||
// Windows doesn't support -lc++ or -lstdc++.
|
||||
// UNSUPPORTED: OS=windows-msvc
|
||||
|
||||
import StdlibUnittest
|
||||
import Fields
|
||||
|
||||
var FieldsTestSuite = TestSuite("Getting and setting fields in base classes")
|
||||
|
||||
FieldsTestSuite.test("Fields from derived from all") {
|
||||
let derived = DerivedFromAll()
|
||||
expectEqual(derived.a, 1)
|
||||
expectEqual(derived.b, 2)
|
||||
expectEqual(derived.c, 3)
|
||||
expectEqual(derived.d, 4)
|
||||
expectEqual(derived.e, 5)
|
||||
expectEqual(derived.f, 6)
|
||||
|
||||
var mutable = DerivedFromAll()
|
||||
mutable.a = 42
|
||||
mutable.d = 44
|
||||
mutable.e = 46
|
||||
mutable.f = 48
|
||||
|
||||
expectEqual(mutable.a, 42)
|
||||
expectEqual(mutable.d, 44)
|
||||
expectEqual(mutable.e, 46)
|
||||
expectEqual(mutable.f, 48)
|
||||
}
|
||||
|
||||
FieldsTestSuite.test("Fields from derived from non trivial") {
|
||||
let derived = NonTrivialDerivedFromAll()
|
||||
expectEqual(derived.a, 1)
|
||||
expectEqual(derived.b, 2)
|
||||
expectEqual(derived.c, 3)
|
||||
expectEqual(derived.d, 4)
|
||||
expectEqual(derived.e, 5)
|
||||
expectEqual(derived.f, 6)
|
||||
|
||||
var mutable = NonTrivialDerivedFromAll()
|
||||
mutable.a = 42
|
||||
mutable.d = 44
|
||||
mutable.e = 46
|
||||
mutable.f = 48
|
||||
|
||||
expectEqual(mutable.a, 42)
|
||||
expectEqual(mutable.d, 44)
|
||||
expectEqual(mutable.e, 46)
|
||||
expectEqual(mutable.f, 48)
|
||||
}
|
||||
|
||||
runAllTests()
|
||||
@@ -0,0 +1,52 @@
|
||||
// RUN: %target-swift-ide-test -print-module -module-to-print=Functions -I %S/Inputs -source-filename=x -enable-cxx-interop | %FileCheck %s
|
||||
|
||||
// CHECK: struct NonTrivial {
|
||||
// CHECK-NEXT: init()
|
||||
// CHECK-NEXT: func inNonTrivial() -> UnsafePointer<CChar>!
|
||||
// CHECK-NEXT: func inNonTrivialWithArgs(_ a: Int32, _ b: Int32) -> UnsafePointer<CChar>!
|
||||
// CHECK-NEXT: }
|
||||
|
||||
// CHECK-NEXT: struct Base {
|
||||
// CHECK-NEXT: init()
|
||||
// CHECK-NEXT: mutating func mutatingInBase() -> UnsafePointer<CChar>!
|
||||
// CHECK-NEXT: func constInBase() -> UnsafePointer<CChar>!
|
||||
// CHECK-NEXT: func takesArgsInBase(_ a: Int32, _ b: Int32, _ c: Int32) -> UnsafePointer<CChar>!
|
||||
// CHECK-NEXT: func takesNonTrivialInBase(_ a: NonTrivial) -> UnsafePointer<CChar>!
|
||||
// CHECK-NEXT: func returnsNonTrivialInBase() -> NonTrivial
|
||||
// CHECK-NEXT: func templateInBase<T>(_ t: T) -> UnsafePointer<CChar>!
|
||||
// CHECK-NEXT: static func staticInBase() -> UnsafePointer<CChar>!
|
||||
// CHECK-NEXT: }
|
||||
|
||||
// CHECK-NEXT: struct OtherBase {
|
||||
// CHECK-NEXT: init()
|
||||
// CHECK-NEXT: func inOtherBase() -> UnsafePointer<CChar>!
|
||||
// CHECK-NEXT: }
|
||||
|
||||
// CHECK-NEXT: struct Derived {
|
||||
// CHECK-NEXT: init()
|
||||
// CHECK-NEXT: func inDerived() -> UnsafePointer<CChar>!
|
||||
// CHECK-NEXT: mutating func mutatingInBase() -> UnsafePointer<CChar>?
|
||||
// CHECK-NEXT: func constInBase() -> UnsafePointer<CChar>?
|
||||
// CHECK-NEXT: func takesArgsInBase(_ a: Int32, _ b: Int32, _ c: Int32) -> UnsafePointer<CChar>?
|
||||
// CHECK-NEXT: func takesNonTrivialInBase(_ a: NonTrivial) -> UnsafePointer<CChar>?
|
||||
// CHECK-NEXT: func returnsNonTrivialInBase() -> NonTrivial
|
||||
// CHECK-NEXT: func inOtherBase() -> UnsafePointer<CChar>?
|
||||
// CHECK-NEXT: }
|
||||
|
||||
// CHECK-NEXT: struct DerivedFromDerived {
|
||||
// CHECK-NEXT: init()
|
||||
// CHECK-NEXT: func topLevel() -> UnsafePointer<CChar>!
|
||||
// CHECK-NEXT: func inDerived() -> UnsafePointer<CChar>?
|
||||
// CHECK-NEXT: mutating func mutatingInBase() -> UnsafePointer<CChar>?
|
||||
// CHECK-NEXT: func constInBase() -> UnsafePointer<CChar>?
|
||||
// CHECK-NEXT: func takesArgsInBase(_ a: Int32, _ b: Int32, _ c: Int32) -> UnsafePointer<CChar>?
|
||||
// CHECK-NEXT: func takesNonTrivialInBase(_ a: NonTrivial) -> UnsafePointer<CChar>?
|
||||
// CHECK-NEXT: func returnsNonTrivialInBase() -> NonTrivial
|
||||
// CHECK-NEXT: func inOtherBase() -> UnsafePointer<CChar>?
|
||||
// CHECK-NEXT: }
|
||||
|
||||
// CHECK-NEXT: struct DerivedFromNonTrivial {
|
||||
// CHECK-NEXT: init()
|
||||
// CHECK-NEXT: func inNonTrivial() -> UnsafePointer<CChar>?
|
||||
// CHECK-NEXT: func inNonTrivialWithArgs(_ a: Int32, _ b: Int32) -> UnsafePointer<CChar>?
|
||||
// CHECK-NEXT: }
|
||||
72
test/Interop/Cxx/class/inheritance/functions.swift
Normal file
72
test/Interop/Cxx/class/inheritance/functions.swift
Normal file
@@ -0,0 +1,72 @@
|
||||
// RUN: %target-run-simple-swift(-I %S/Inputs/ -Xfrontend -enable-cxx-interop)
|
||||
//
|
||||
// REQUIRES: executable_test
|
||||
|
||||
import StdlibUnittest
|
||||
import Functions
|
||||
|
||||
var FunctionsTestSuite = TestSuite("Calling functions in base classes")
|
||||
|
||||
FunctionsTestSuite.test("Basic methods from derived") {
|
||||
let derived = Derived()
|
||||
expectEqual(String(cString: derived.constInBase()!), "Base::constInBase")
|
||||
expectEqual(String(cString: derived.takesArgsInBase(0, 1, 2)!), "Base::takesArgsInBase")
|
||||
|
||||
var mutableDerived = derived
|
||||
expectEqual(String(cString: mutableDerived.mutatingInBase()!), "Base::mutatingInBase")
|
||||
}
|
||||
|
||||
FunctionsTestSuite.test("NonTrivial methods from derived") {
|
||||
let derived = Derived()
|
||||
expectEqual(String(cString: derived.takesNonTrivialInBase(NonTrivial())!), "Base::takesNonTrivialInBase")
|
||||
_ = derived.returnsNonTrivialInBase()
|
||||
}
|
||||
|
||||
// TODO: eventually support templates.
|
||||
//FunctionsTestSuite.test("Template from derived") {
|
||||
// let derived = Derived()
|
||||
// expectEqual(String(cString: derived.templateInBase(0)!), "Base::templateInBase")
|
||||
//}
|
||||
|
||||
// TODO: eventually support static functions.
|
||||
//FunctionsTestSuite.test("Static from derived") {
|
||||
// expectEqual(String(cString: Derived.staticInBase()!), "Base::staticInBase")
|
||||
//}
|
||||
|
||||
FunctionsTestSuite.test("Other base member from derived") {
|
||||
let derived = Derived()
|
||||
expectEqual(String(cString: derived.inOtherBase()!), "OtherBase::inOtherBase")
|
||||
}
|
||||
|
||||
FunctionsTestSuite.test("Basic methods from derived * 2") {
|
||||
let dd = DerivedFromDerived()
|
||||
expectEqual(String(cString: dd.constInBase()!), "Base::constInBase")
|
||||
expectEqual(String(cString: dd.takesArgsInBase(0, 1, 2)!), "Base::takesArgsInBase")
|
||||
|
||||
var mutableDerived = dd
|
||||
expectEqual(String(cString: mutableDerived.mutatingInBase()!), "Base::mutatingInBase")
|
||||
}
|
||||
|
||||
FunctionsTestSuite.test("NonTrivial methods from derived * 2") {
|
||||
let dd = DerivedFromDerived()
|
||||
expectEqual(String(cString: dd.takesNonTrivialInBase(NonTrivial())!), "Base::takesNonTrivialInBase")
|
||||
_ = dd.returnsNonTrivialInBase()
|
||||
}
|
||||
|
||||
// TODO: eventually support static functions.
|
||||
//FunctionsTestSuite.test("Static from derived * 2") {
|
||||
// expectEqual(String(cString: DerivedFromDerived.staticInBase()!), "Base::staticInBase")
|
||||
//}
|
||||
|
||||
FunctionsTestSuite.test("Other base member from derived * 2") {
|
||||
let dd = DerivedFromDerived()
|
||||
expectEqual(String(cString: dd.inOtherBase()!), "OtherBase::inOtherBase")
|
||||
}
|
||||
|
||||
FunctionsTestSuite.test("base member from derived from non trivial") {
|
||||
let dnt = DerivedFromNonTrivial()
|
||||
expectEqual(String(cString: dnt.inNonTrivial()!), "NonTrivial::inNonTrivial")
|
||||
expectEqual(String(cString: dnt.inNonTrivialWithArgs(0, 1)!), "NonTrivial::inNonTrivialWithArgs")
|
||||
}
|
||||
|
||||
runAllTests()
|
||||
@@ -0,0 +1,49 @@
|
||||
// RUN: %target-swift-ide-test -print-module -module-to-print=SubTypes -I %S/Inputs -source-filename=x -enable-cxx-interop | %FileCheck %s
|
||||
|
||||
// CHECK: struct Base {
|
||||
// CHECK-NEXT: init()
|
||||
// CHECK-NEXT: enum EnumClass : CChar {
|
||||
// CHECK-NEXT: init?(rawValue: CChar)
|
||||
// CHECK-NEXT: var rawValue: CChar { get }
|
||||
// CHECK-NEXT: typealias RawValue = CChar
|
||||
// CHECK-NEXT: case eca
|
||||
// CHECK-NEXT: case ecb
|
||||
// CHECK-NEXT: case ecc
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: struct Enum : Equatable, RawRepresentable {
|
||||
// CHECK-NEXT: init(_ rawValue: UInt32)
|
||||
// CHECK-NEXT: init(rawValue: UInt32)
|
||||
// CHECK-NEXT: var rawValue: UInt32
|
||||
// CHECK-NEXT: typealias RawValue = UInt32
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: struct Struct {
|
||||
// CHECK-NEXT: init()
|
||||
// CHECK-NEXT: init(sa: Int32, sb: Int32)
|
||||
// CHECK-NEXT: var sa: Int32
|
||||
// CHECK-NEXT: var sb: Int32
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: struct Parent {
|
||||
// CHECK-NEXT: init()
|
||||
// CHECK-NEXT: struct Child {
|
||||
// CHECK-NEXT: init()
|
||||
// CHECK-NEXT: init(pca: Int32)
|
||||
// CHECK-NEXT: var pca: Int32
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: struct Union {
|
||||
// CHECK-NEXT: init()
|
||||
// CHECK-NEXT: init(ua: Int32)
|
||||
// CHECK-NEXT: init(ub: Base.Struct)
|
||||
// CHECK-NEXT: var ua: Int32
|
||||
// CHECK-NEXT: var ub: Base.Struct
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: }
|
||||
|
||||
// CHECK-NEXT: struct Derived {
|
||||
// CHECK-NEXT: init()
|
||||
// CHECK-NEXT: typealias EnumClass = Base.EnumClass.Type
|
||||
// CHECK-NEXT: typealias Enum = Base.Enum.Type
|
||||
// CHECK-NEXT: typealias Struct = Base.Struct.Type
|
||||
// CHECK-NEXT: typealias Parent = Base.Parent.Type
|
||||
// CHECK-NEXT: typealias Union = Base.Union.Type
|
||||
// CHECK-NEXT: }
|
||||
@@ -0,0 +1,17 @@
|
||||
// RUN: %target-swift-ide-test -print-module -module-to-print=TypeAliases -I %S/Inputs -source-filename=x -enable-cxx-interop | %FileCheck %s
|
||||
|
||||
// CHECK: struct Base {
|
||||
// CHECK-NEXT: init()
|
||||
// CHECK-NEXT: struct Struct {
|
||||
// CHECK-NEXT: init()
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: typealias T = Int32
|
||||
// CHECK-NEXT: typealias U = Base.Struct
|
||||
// CHECK-NEXT: }
|
||||
|
||||
// CHECK-NEXT: struct Derived {
|
||||
// CHECK-NEXT: init()
|
||||
// CHECK-NEXT: typealias Struct = Base.Struct.Type
|
||||
// CHECK-NEXT: typealias T = Int32
|
||||
// CHECK-NEXT: typealias U = Base.Struct
|
||||
// CHECK-NEXT: }
|
||||
Reference in New Issue
Block a user