Skip to content

Commit 08e7160

Browse files
committed
[cxx-interop] Support templated C++ constructors.
1 parent 178dac0 commit 08e7160

File tree

8 files changed

+112
-12
lines changed

8 files changed

+112
-12
lines changed

include/swift/AST/Decl.h

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6509,9 +6509,12 @@ class ConstructorDecl : public AbstractFunctionDecl {
65096509
GenericParamList *GenericParams,
65106510
DeclContext *Parent);
65116511

6512-
template<class ...Args>
6513-
static ConstructorDecl *createImported(ASTContext &ctx, ClangNode clangNode,
6514-
Args&&... args);
6512+
static ConstructorDecl *
6513+
createImported(ASTContext &ctx, ClangNode clangNode, DeclName name,
6514+
SourceLoc constructorLoc, bool failable,
6515+
SourceLoc failabilityLoc, bool throws, SourceLoc throwsLoc,
6516+
ParameterList *bodyParams, GenericParamList *genericParams,
6517+
DeclContext *parent);
65156518

65166519
SourceLoc getConstructorLoc() const { return getNameLoc(); }
65176520
SourceLoc getStartLoc() const { return getConstructorLoc(); }

lib/AST/Decl.cpp

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7524,11 +7524,16 @@ ConstructorDecl::ConstructorDecl(DeclName Name, SourceLoc ConstructorLoc,
75247524
assert(Name.getBaseName() == DeclBaseName::createConstructor());
75257525
}
75267526

7527-
template<class ...Args>
7528-
ConstructorDecl *ConstructorDecl::createImported(ASTContext &ctx,
7529-
ClangNode clangNode,
7530-
Args&&... args) {
7531-
auto ctor = new (ctx) ConstructorDecl(std::forward<Args>(args)...);
7527+
ConstructorDecl *ConstructorDecl::createImported(
7528+
ASTContext &ctx, ClangNode clangNode, DeclName name,
7529+
SourceLoc constructorLoc, bool failable, SourceLoc failabilityLoc,
7530+
bool throws, SourceLoc throwsLoc, ParameterList *bodyParams,
7531+
GenericParamList *genericParams, DeclContext *parent) {
7532+
void *declPtr = allocateMemoryForDecl<ConstructorDecl>(
7533+
ctx, sizeof(ConstructorDecl), true);
7534+
auto ctor = ::new (declPtr)
7535+
ConstructorDecl(name, constructorLoc, failable, failabilityLoc, throws,
7536+
throwsLoc, bodyParams, genericParams, parent);
75327537
ctor->setClangNode(clangNode);
75337538
return ctor;
75347539
}

lib/Sema/CSApply.cpp

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -107,12 +107,19 @@ Solution::computeSubstitutions(GenericSignature sig,
107107
}
108108

109109
static ConcreteDeclRef generateDeclRefForSpecializedCXXFunctionTemplate(
110-
ASTContext &ctx, FuncDecl *oldDecl, SubstitutionMap subst,
110+
ASTContext &ctx, AbstractFunctionDecl *oldDecl, SubstitutionMap subst,
111111
clang::FunctionDecl *specialized) {
112112
// Create a new ParameterList with the substituted type.
113113
auto oldFnType =
114114
cast<GenericFunctionType>(oldDecl->getInterfaceType().getPointer());
115115
auto newFnType = oldFnType->substGenericArgs(subst);
116+
// The constructor type is a function type as follows:
117+
// (CType.Type) -> (Generic) -> CType
118+
// But we only want the result of that function type because that is the
119+
// function type with the generic params that need to be substituted:
120+
// (Generic) -> CType
121+
if (isa<ConstructorDecl>(oldDecl))
122+
newFnType = cast<FunctionType>(newFnType->getResult().getPointer());
116123
SmallVector<ParamDecl *, 4> newParams;
117124
unsigned i = 0;
118125
for (auto paramTy : newFnType->getParams()) {
@@ -126,6 +133,16 @@ static ConcreteDeclRef generateDeclRefForSpecializedCXXFunctionTemplate(
126133
auto *newParamList =
127134
ParameterList::create(ctx, SourceLoc(), newParams, SourceLoc());
128135

136+
if (isa<ConstructorDecl>(oldDecl)) {
137+
DeclName ctorName(ctx, DeclBaseName::createConstructor(), newParamList);
138+
auto newCtorDecl = ConstructorDecl::createImported(
139+
ctx, specialized, ctorName, oldDecl->getLoc(), /*failable=*/false,
140+
/*failabilityLoc=*/SourceLoc(), /*throws=*/false,
141+
/*throwsLoc=*/SourceLoc(), newParamList, /*genericParams=*/nullptr,
142+
oldDecl->getDeclContext());
143+
return ConcreteDeclRef(newCtorDecl);
144+
}
145+
129146
// Generate a name for the specialized function.
130147
std::string newNameStr;
131148
llvm::raw_string_ostream buffer(newNameStr);
@@ -139,8 +156,8 @@ static ConcreteDeclRef generateDeclRefForSpecializedCXXFunctionTemplate(
139156

140157
auto newFnDecl = FuncDecl::createImported(
141158
ctx, oldDecl->getLoc(), newName, oldDecl->getNameLoc(),
142-
/*Async*/ false, oldDecl->hasThrows(), newParamList,
143-
newFnType->getResult(), /*GenericParams*/ nullptr,
159+
/*Async=*/false, oldDecl->hasThrows(), newParamList,
160+
newFnType->getResult(), /*GenericParams=*/nullptr,
144161
oldDecl->getDeclContext(), specialized);
145162
return ConcreteDeclRef(newFnDecl);
146163
}
@@ -168,7 +185,7 @@ Solution::resolveConcreteDeclRef(ValueDecl *decl,
168185
cast<clang::FunctionTemplateDecl>(decl->getClangDecl())),
169186
subst);
170187
return generateDeclRefForSpecializedCXXFunctionTemplate(
171-
decl->getASTContext(), cast<FuncDecl>(decl), subst, newFn);
188+
decl->getASTContext(), cast<AbstractFunctionDecl>(decl), subst, newFn);
172189
}
173190

174191
return ConcreteDeclRef(decl, subst);

test/Interop/Cxx/class/Inputs/constructors.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,22 @@ struct IntWrapper {
4444
int x;
4545
};
4646

47+
struct TemplatedConstructor {
48+
ArgType value;
49+
50+
template<class T>
51+
TemplatedConstructor(T value) : value(value) { }
52+
};
53+
54+
struct TemplatedConstructorWithExtraArg {
55+
template<class T>
56+
TemplatedConstructorWithExtraArg(int, T value) { }
57+
template<class T>
58+
TemplatedConstructorWithExtraArg(T value, int) { }
59+
template<class T, class U>
60+
TemplatedConstructorWithExtraArg(T value, U other) { }
61+
};
62+
4763
// TODO: we should be able to import this constructor correctly. Until we can,
4864
// make sure not to crash.
4965
struct UsingBaseConstructor : ConstructorWithParam {

test/Interop/Cxx/class/constructors-executable.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,11 @@ CxxConstructorTestSuite.test("ConstructorWithParam") {
3131
expectEqual(44, instance.x)
3232
}
3333

34+
CxxConstructorTestSuite.test("TemplatedConstructor") {
35+
let arg = ArgType(i: 2)
36+
let instance = TemplatedConstructor(arg)
37+
38+
expectEqual(2, instance.value.i)
39+
}
40+
3441
runAllTests()

test/Interop/Cxx/class/constructors-irgen.swift

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,3 +81,33 @@ public func createStructWithSubobjectCopyConstructorAndValue() {
8181
let member = StructWithCopyConstructorAndValue()
8282
let obj = StructWithSubobjectCopyConstructorAndValue(member: member)
8383
}
84+
85+
public func createTemplatedConstructor() {
86+
// ITANIUM_X64-LABEL: define swiftcc void @"$ss26createTemplatedConstructoryyF"()
87+
// ITANIUM_X64: [[OBJ:%.*]] = alloca %TSo20TemplatedConstructorV
88+
// ITANIUM_X64: [[IVAL:%.*]] = load i32, i32*
89+
// ITANIUM_X64: [[OBJ_AS_STRUCT:%.*]] = bitcast %TSo20TemplatedConstructorV* [[OBJ]] to %struct.TemplatedConstructor*
90+
// ITANIUM_X64: call void @_ZN20TemplatedConstructorC1I7ArgTypeEET_(%struct.TemplatedConstructor* [[OBJ_AS_STRUCT]], i32 [[IVAL]])
91+
// ITANIUM_X64: ret void
92+
93+
// ITANIUM_X64-LABEL: define linkonce_odr void @_ZN20TemplatedConstructorC1I7ArgTypeEET_(%struct.TemplatedConstructor* %this, i32 %value.coerce)
94+
95+
// ITANIUM_ARM-LABEL: define protected swiftcc void @"$ss26createTemplatedConstructoryyF"()
96+
// ITANIUM_ARM: [[OBJ:%.*]] = alloca %TSo20TemplatedConstructorV
97+
// ITANIUM_ARM: [[IVAL:%.*]] = load [1 x i32], [1 x i32]*
98+
// ITANIUM_ARM: [[OBJ_AS_STRUCT:%.*]] = bitcast %TSo20TemplatedConstructorV* [[OBJ]] to %struct.TemplatedConstructor*
99+
// ITANIUM_ARM: call %struct.TemplatedConstructor* @_ZN20TemplatedConstructorC2I7ArgTypeEET_(%struct.TemplatedConstructor* [[OBJ_AS_STRUCT]], [1 x i32] [[IVAL]])
100+
// ITANIUM_ARM: ret void
101+
102+
// ITANIUM_ARM-LABEL: define linkonce_odr %struct.TemplatedConstructor* @_ZN20TemplatedConstructorC2I7ArgTypeEET_(%struct.TemplatedConstructor* returned %this, [1 x i32] %value.coerce)
103+
104+
// MICROSOFT_X64-LABEL: define dllexport swiftcc void @"$ss26createTemplatedConstructoryyF"()
105+
// MICROSOFT_X64: [[OBJ:%.*]] = alloca %TSo20TemplatedConstructorV
106+
// MICROSOFT_X64: [[IVAL:%.*]] = load i32, i32*
107+
// MICROSOFT_X64: [[OBJ_AS_STRUCT:%.*]] = bitcast %TSo20TemplatedConstructorV* [[OBJ]] to %struct.TemplatedConstructor*
108+
// MICROSOFT_X64: call %struct.TemplatedConstructor* @"??$?0UArgType@@@TemplatedConstructor@@QEAA@UArgType@@@Z"(%struct.TemplatedConstructor* [[OBJ_AS_STRUCT]], i32 [[IVAL]])
109+
// MICROSOFT_X64: ret void
110+
111+
// MICROSOFT_X64-LABEL: define linkonce_odr dso_local %struct.TemplatedConstructor* @"??$?0UArgType@@@TemplatedConstructor@@QEAA@UArgType@@@Z"(%struct.TemplatedConstructor* returned %this, i32 %value.coerce)
112+
let templated = TemplatedConstructor(ArgType())
113+
}

test/Interop/Cxx/class/constructors-module-interface.swift

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,12 @@
3636
// CHECK-NEXT: var i: Int32
3737
// CHECK-NEXT: init(_ Arg: ArgType)
3838
// CHECK-NEXT: }
39+
// CHECK: struct TemplatedConstructor {
40+
// CHECK-NEXT: var value: ArgType
41+
// CHECK-NEXT: init<T>(_ value: T)
42+
// CHECK-NEXT: }
43+
// CHECK: struct TemplatedConstructorWithExtraArg {
44+
// CHECK-NEXT: init<T>(_: Int32, _ value: T)
45+
// CHECK-NEXT: init<T>(_ value: T, _: Int32)
46+
// CHECK-NEXT: init<T, U>(_ value: T, _ other: U)
47+
// CHECK-NEXT: }

test/Interop/Cxx/class/constructors-silgen.swift

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,3 +56,16 @@ public func singleMemberTypeValueInit() {
5656
public func deletedConstructor(a: UnsafeMutablePointer<Int32>) {
5757
let deletedExplicitly = DefaultConstructorDeleted(a: a)
5858
}
59+
60+
// CHECK-LABEL: sil [ossa] @$s4main20templatedConstructoryyF : $@convention(thin) () -> ()
61+
// CHECK: [[TEMPL:%.*]] = alloc_stack $TemplatedConstructor
62+
// CHECK: [[ARG:%.*]] = alloc_stack $ArgType
63+
// CHECK: [[ARG_VAL:%.*]] = load [trivial] [[ARG]] : $*ArgType
64+
// CHECK: [[FN:%.*]] = function_ref @{{_ZN20TemplatedConstructorC1I7ArgTypeEET_|\?\?\$\?0UArgType@@@TemplatedConstructor@@QEAA@UArgType@@@Z}} : $@convention(c) (ArgType) -> @out TemplatedConstructor
65+
// CHECK: apply [[FN]]([[TEMPL]], [[ARG_VAL]]) : $@convention(c) (ArgType) -> @out TemplatedConstructor
66+
// CHECK-LABEL: end sil function '$s4main20templatedConstructoryyF'
67+
68+
// CHECK-LABEL: sil hidden_external [clang TemplatedConstructor.init] @{{_ZN20TemplatedConstructorC1I7ArgTypeEET_|\?\?\$\?0UArgType@@@TemplatedConstructor@@QEAA@UArgType@@@Z}} : $@convention(c) (ArgType) -> @out TemplatedConstructor
69+
public func templatedConstructor() {
70+
let templated = TemplatedConstructor(ArgType())
71+
}

0 commit comments

Comments
 (0)