Skip to content

Commit a5e953b

Browse files
martinboehmezoecarver
authored andcommitted
Add support for calling C++ constructors.
Because C++ constructors always take a `this` pointer to the object to be initialized, we mark the SIL function return type with the `@out` attribute. On the IRGen side, we retrofit support for formal indirect return values as well as thin metatypes.
1 parent 171e0e2 commit a5e953b

14 files changed

+4547
-29
lines changed

lib/ClangImporter/ClangImporter.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3589,7 +3589,11 @@ void ClangImporter::getMangledName(raw_ostream &os,
35893589
if (!Impl.Mangler)
35903590
Impl.Mangler.reset(Impl.getClangASTContext().createMangleContext());
35913591

3592-
Impl.Mangler->mangleName(clangDecl, os);
3592+
if (auto ctor = dyn_cast<clang::CXXConstructorDecl>(clangDecl)) {
3593+
Impl.Mangler->mangleCXXCtor(ctor, clang::Ctor_Complete, os);
3594+
} else {
3595+
Impl.Mangler->mangleName(clangDecl, os);
3596+
}
35933597
}
35943598

35953599
// ---------------------------------------------------------------------------

lib/ClangImporter/ImportDecl.cpp

Lines changed: 67 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3378,6 +3378,11 @@ namespace {
33783378
continue;
33793379
}
33803380

3381+
if (auto CD = dyn_cast<ConstructorDecl>(member)) {
3382+
ctors.push_back(CD);
3383+
continue;
3384+
}
3385+
33813386
if (auto MD = dyn_cast<FuncDecl>(member)) {
33823387
methods.push_back(MD);
33833388
continue;
@@ -3434,12 +3439,14 @@ namespace {
34343439

34353440
bool hasReferenceableFields = !members.empty();
34363441

3437-
if (hasZeroInitializableStorage) {
3442+
if (hasZeroInitializableStorage &&
3443+
!Impl.SwiftContext.LangOpts.EnableCXXInterop) {
34383444
// Add constructors for the struct.
34393445
ctors.push_back(createDefaultConstructor(Impl, result));
34403446
}
34413447

3442-
if (hasReferenceableFields && hasMemberwiseInitializer) {
3448+
if (hasReferenceableFields && hasMemberwiseInitializer &&
3449+
!Impl.SwiftContext.LangOpts.EnableCXXInterop) {
34433450
// The default zero initializer suppresses the implicit value
34443451
// constructor that would normally be formed, so we have to add that
34453452
// explicitly as well.
@@ -3491,6 +3498,28 @@ namespace {
34913498
return result;
34923499
}
34933500

3501+
Decl *VisitCXXRecordDecl(const clang::CXXRecordDecl *decl) {
3502+
auto &clangSema = Impl.getClangSema();
3503+
// Make Clang define the implicit default constructor if the class needs
3504+
// it. Make sure we only do this if the class has been fully defined and
3505+
// we're not in a dependent context (this is equivalent to the logic in
3506+
// CanDeclareSpecialMemberFunction in Clang's SemaLookup.cpp).
3507+
if (decl->getDefinition() && !decl->isBeingDefined() &&
3508+
!decl->isDependentContext() &&
3509+
decl->needsImplicitDefaultConstructor()) {
3510+
// Casting away const here should be OK because
3511+
// SwiftDeclConverter::Visit() is in practice called with a non-const
3512+
// argument.
3513+
clang::CXXConstructorDecl *ctor =
3514+
clangSema.DeclareImplicitDefaultConstructor(
3515+
const_cast<clang::CXXRecordDecl *>(decl));
3516+
clangSema.DefineImplicitDefaultConstructor(clang::SourceLocation(),
3517+
ctor);
3518+
}
3519+
3520+
return VisitRecordDecl(decl);
3521+
}
3522+
34943523
Decl *VisitClassTemplateSpecializationDecl(
34953524
const clang::ClassTemplateSpecializationDecl *decl) {
34963525
// `Sema::isCompleteType` will try to instantiate the class template as a
@@ -3745,6 +3774,9 @@ namespace {
37453774
ImportedName importedName,
37463775
Optional<ImportedName> correctSwiftName,
37473776
Optional<AccessorInfo> accessorInfo) {
3777+
if (decl->isDeleted())
3778+
return nullptr;
3779+
37483780
auto dc =
37493781
Impl.importDeclContextOf(decl, importedName.getEffectiveContext());
37503782
if (!dc)
@@ -3753,7 +3785,6 @@ namespace {
37533785
DeclName name = accessorInfo ? DeclName() : importedName.getDeclName();
37543786
auto selfIdx = importedName.getSelfIndex();
37553787

3756-
FuncDecl *result = nullptr;
37573788
ImportedType importedType;
37583789
bool selfIsInOut = false;
37593790
ParameterList *bodyParams = nullptr;
@@ -3855,27 +3886,44 @@ namespace {
38553886
if (!importedType)
38563887
return nullptr;
38573888

3858-
auto resultTy = importedType.getType();
38593889
auto loc = Impl.importSourceLoc(decl->getLocation());
38603890

38613891
// FIXME: Poor location info.
38623892
auto nameLoc = Impl.importSourceLoc(decl->getLocation());
3863-
result = createFuncOrAccessor(
3864-
Impl.SwiftContext, loc, accessorInfo, name,
3865-
nameLoc, bodyParams, resultTy,
3866-
/*async*/ false, /*throws*/ false, dc, decl);
3867-
3868-
if (!dc->isModuleScopeContext()) {
3869-
if (selfIsInOut)
3870-
result->setSelfAccessKind(SelfAccessKind::Mutating);
3871-
else
3872-
result->setSelfAccessKind(SelfAccessKind::NonMutating);
3873-
if (selfIdx) {
3874-
result->setSelfIndex(selfIdx.getValue());
3875-
} else {
3876-
result->setStatic();
3877-
result->setImportAsStaticMember();
3893+
3894+
AbstractFunctionDecl *result = nullptr;
3895+
if (auto *ctordecl = dyn_cast<clang::CXXConstructorDecl>(decl)) {
3896+
// TODO: Is failable, throws etc. correct?
3897+
DeclName ctorName(Impl.SwiftContext, DeclBaseName::createConstructor(),
3898+
bodyParams);
3899+
result = Impl.createDeclWithClangNode<ConstructorDecl>(
3900+
decl, AccessLevel::Public, ctorName, loc, /*failable=*/false,
3901+
/*FailabilityLoc=*/SourceLoc(), /*Throws=*/false,
3902+
/*ThrowsLoc=*/SourceLoc(), bodyParams, /*GenericParams=*/nullptr,
3903+
dc);
3904+
} else {
3905+
auto resultTy = importedType.getType();
3906+
3907+
FuncDecl *func = createFuncOrAccessor(
3908+
Impl.SwiftContext, loc, accessorInfo, name,
3909+
nameLoc, bodyParams, resultTy,
3910+
/*async*/ false, /*throws*/ false, dc, decl);
3911+
result = func;
3912+
3913+
if (!dc->isModuleScopeContext()) {
3914+
if (selfIsInOut)
3915+
func->setSelfAccessKind(SelfAccessKind::Mutating);
3916+
else
3917+
func->setSelfAccessKind(SelfAccessKind::NonMutating);
3918+
if (selfIdx) {
3919+
func->setSelfIndex(selfIdx.getValue());
3920+
} else {
3921+
func->setStatic();
3922+
func->setImportAsStaticMember();
3923+
}
38783924
}
3925+
// Someday, maybe this will need to be 'open' for C++ virtual methods.
3926+
func->setAccess(AccessLevel::Public);
38793927
}
38803928

38813929
result->setIsObjC(false);
@@ -3889,8 +3937,6 @@ namespace {
38893937
result->getAttrs().add(new (Impl.SwiftContext)
38903938
FinalAttr(/*IsImplicit=*/true));
38913939

3892-
// Someday, maybe this will need to be 'open' for C++ virtual methods.
3893-
result->setAccess(AccessLevel::Public);
38943940
finishFuncDecl(decl, result);
38953941

38963942
// If this is a compatibility stub, mark it as such.

lib/ClangImporter/ImportName.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1600,6 +1600,20 @@ ImportedName NameImporter::importNameImpl(const clang::NamedDecl *D,
16001600
ArrayRef<const clang::ParmVarDecl *> params;
16011601
switch (D->getDeclName().getNameKind()) {
16021602
case clang::DeclarationName::CXXConstructorName:
1603+
isInitializer = true;
1604+
isFunction = true;
1605+
result.info.initKind = CtorInitializerKind::Designated;
1606+
baseName = "init";
1607+
// Add empty argument names.
1608+
if (auto ctor = dyn_cast<clang::CXXConstructorDecl>(D)) {
1609+
for (size_t i = 0; i < ctor->param_size(); ++i) {
1610+
argumentNames.push_back(StringRef());
1611+
}
1612+
if (ctor->isVariadic())
1613+
argumentNames.push_back(StringRef());
1614+
}
1615+
break;
1616+
16031617
case clang::DeclarationName::CXXConversionFunctionName:
16041618
case clang::DeclarationName::CXXDestructorName:
16051619
case clang::DeclarationName::CXXLiteralOperatorName:

lib/IRGen/GenCall.cpp

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -338,10 +338,12 @@ llvm::CallingConv::ID irgen::expandCallingConv(IRGenModule &IGM,
338338

339339
static void addIndirectResultAttributes(IRGenModule &IGM,
340340
llvm::AttributeList &attrs,
341-
unsigned paramIndex, bool allowSRet) {
341+
unsigned paramIndex, bool allowSRet,
342+
bool noCapture = true) {
342343
llvm::AttrBuilder b;
343344
b.addAttribute(llvm::Attribute::NoAlias);
344-
b.addAttribute(llvm::Attribute::NoCapture);
345+
if (noCapture)
346+
b.addAttribute(llvm::Attribute::NoCapture);
345347
if (allowSRet)
346348
b.addAttribute(llvm::Attribute::StructRet);
347349
attrs = attrs.addAttributes(IGM.getLLVMContext(),
@@ -428,7 +430,7 @@ namespace {
428430

429431
private:
430432
void expand(SILParameterInfo param);
431-
llvm::Type *addIndirectResult();
433+
llvm::Type *addIndirectResult(bool noCapture = true);
432434

433435
SILFunctionConventions getSILFuncConventions() const {
434436
return SILFunctionConventions(FnType, IGM.getSILModule());
@@ -482,7 +484,8 @@ llvm::Type *SignatureExpansion::addIndirectResult() {
482484
auto resultType = getSILFuncConventions().getSILResultType(
483485
IGM.getMaximalTypeExpansionContext());
484486
const TypeInfo &resultTI = IGM.getTypeInfo(resultType);
485-
addIndirectResultAttributes(IGM, Attrs, ParamIRTypes.size(), claimSRet());
487+
addIndirectResultAttributes(IGM, Attrs, ParamIRTypes.size(), claimSRet(),
488+
noCapture);
486489
addPointerParameter(resultTI.getStorageType());
487490
return IGM.VoidTy;
488491
}
@@ -1306,6 +1309,9 @@ void SignatureExpansion::expandExternalSignatureTypes() {
13061309
// Convert each parameter to a Clang type.
13071310
for (auto param : params) {
13081311
auto clangTy = IGM.getClangType(param, FnType);
1312+
if (clangTy->isVoidType()) {
1313+
continue;
1314+
}
13091315
paramTys.push_back(clangTy);
13101316
}
13111317

@@ -1330,8 +1336,17 @@ void SignatureExpansion::expandExternalSignatureTypes() {
13301336
}
13311337

13321338
// If we return indirectly, that is the first parameter type.
1333-
if (returnInfo.isIndirect()) {
1334-
addIndirectResult();
1339+
bool formalIndirect = FnType->getNumResults() > 0 &&
1340+
FnType->getSingleResult().isFormalIndirect();
1341+
if (formalIndirect || returnInfo.isIndirect()) {
1342+
// Specify "nocapture" if we're returning the result indirectly for
1343+
// low-level ABI reasons, as the called function never sees the implicit
1344+
// output parameter.
1345+
// On the other hand, if the result is a formal indirect result in SIL, that
1346+
// means that the Clang function has an explicit output parameter (e.g. it's
1347+
// a C++ constructor), which it might capture -- so don't specify
1348+
// "nocapture" in that case.
1349+
addIndirectResult(/* noCapture = */ returnInfo.isIndirect());
13351350
}
13361351

13371352
size_t firstParamToLowerNormally = 0;

lib/IRGen/GenClangType.cpp

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -403,7 +403,19 @@ clang::CanQualType GenClangType::visitProtocolType(CanProtocolType type) {
403403
}
404404

405405
clang::CanQualType GenClangType::visitMetatypeType(CanMetatypeType type) {
406-
return getClangMetatypeType(getClangASTContext());
406+
assert(type->hasRepresentation() &&
407+
"metatype should have been assigned a representation by SIL");
408+
switch (type->getRepresentation()) {
409+
case MetatypeRepresentation::Thin:
410+
return getClangASTContext().VoidTy;
411+
412+
case MetatypeRepresentation::Thick:
413+
llvm_unreachable("thick metatypes don't have a corresponding Clang type");
414+
415+
case MetatypeRepresentation::ObjC:
416+
return getClangMetatypeType(getClangASTContext());
417+
}
418+
llvm_unreachable("bad representation");
407419
}
408420

409421
clang::CanQualType

lib/SIL/IR/SILFunctionType.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2788,6 +2788,27 @@ class CXXMethodConventions : public CFunctionTypeConventions {
27882788
return ParameterConvention::Indirect_In_Guaranteed;
27892789
return ParameterConvention::Indirect_Inout;
27902790
}
2791+
ResultConvention getResult(const TypeLowering &resultTL) const override {
2792+
if (isa<clang::CXXConstructorDecl>(TheDecl)) {
2793+
// Represent the `this` pointer as an indirectly returned result.
2794+
// This gets us most of the way towards representing the ABI of a
2795+
// constructor correctly, but it's not guaranteed to be entirely correct.
2796+
// C++ constructor ABIs are complicated and can require passing additional
2797+
// "implicit" arguments that depend not only on the signature of the
2798+
// constructor but on the class on which it's defined (e.g. whether that
2799+
// class has a virtual base class).
2800+
// Effectively, we're making an assumption here that there are no implicit
2801+
// arguments and that the return type of the constructor ABI is void (and
2802+
// indeed we have no way to represent anything else here). If this assumed
2803+
// ABI doesn't match the actual ABI, we insert a thunk in IRGen. On some
2804+
// ABIs (e.g. Itanium x64), we get lucky and the ABI for a complete
2805+
// constructor call always matches the ABI we assume here. Even if the
2806+
// actual ABI doesn't match the assumed ABI, we try to get as close as
2807+
// possible to make it easy for LLVM to optimize away the thunk.
2808+
return ResultConvention::Indirect;
2809+
}
2810+
return CFunctionTypeConventions::getResult(resultTL);
2811+
}
27912812
static bool classof(const Conventions *C) {
27922813
return C->getKind() == ConventionsKind::CXXMethod;
27932814
}

0 commit comments

Comments
 (0)