Skip to content

Commit 3258688

Browse files
committed
[cxx-interop] Do not import constructors of abstract C++ classes
Clang rejects code that tries to call a constructor of an abstract C++ class with an error: "Variable type 'Base' is an abstract class". Swift should reject this as well. rdar://119689243
1 parent 2b2beb6 commit 3258688

File tree

4 files changed

+37
-3
lines changed

4 files changed

+37
-3
lines changed

lib/ClangImporter/ImportDecl.cpp

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2371,8 +2371,9 @@ namespace {
23712371

23722372
bool needsEmptyInitializer = true;
23732373
if (cxxRecordDecl) {
2374-
needsEmptyInitializer = !cxxRecordDecl->hasDefaultConstructor() ||
2375-
cxxRecordDecl->ctors().empty();
2374+
needsEmptyInitializer = !cxxRecordDecl->isAbstract() &&
2375+
(!cxxRecordDecl->hasDefaultConstructor() ||
2376+
cxxRecordDecl->ctors().empty());
23762377
}
23772378
if (hasZeroInitializableStorage && needsEmptyInitializer) {
23782379
// Add default constructor for the struct if compiling in C mode.
@@ -3746,6 +3747,19 @@ namespace {
37463747

37473748
Decl *VisitCXXMethodDecl(const clang::CXXMethodDecl *decl) {
37483749
auto method = VisitFunctionDecl(decl);
3750+
3751+
// Do not expose constructors of abstract C++ classes.
3752+
if (auto recordDecl =
3753+
dyn_cast<clang::CXXRecordDecl>(decl->getDeclContext())) {
3754+
if (isa<clang::CXXConstructorDecl>(decl) && recordDecl->isAbstract() &&
3755+
isa_and_nonnull<ValueDecl>(method)) {
3756+
Impl.markUnavailable(
3757+
cast<ValueDecl>(method),
3758+
"constructors of abstract C++ classes are unavailable in Swift");
3759+
return method;
3760+
}
3761+
}
3762+
37493763
if (decl->isVirtual() && isa_and_nonnull<ValueDecl>(method)) {
37503764

37513765
if (auto dc = method->getDeclContext();

test/Interop/Cxx/class/inheritance/Inputs/virtual-methods.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ struct Base3 { virtual int f() { return 24; } };
1313
struct Derived2 : public Base2 { virtual int f() { return 42; } };
1414
struct Derived3 : public Base3 { virtual int f() { return 42; } };
1515
struct Derived4 : public Base3 { };
16+
struct DerivedFromDerived2 : public Derived2 {};
1617

1718
template <class T>
1819
struct Derived : Base {

test/Interop/Cxx/class/inheritance/virtual-methods-module-interface.swift

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,19 @@
11
// RUN: %target-swift-ide-test -print-module -print-implicit-attrs -module-to-print=VirtualMethods -I %S/Inputs -source-filename=x -enable-experimental-cxx-interop | %FileCheck %s
22

33
// CHECK: struct Base {
4-
// CHECK-NEXT: init()
4+
// CHECK-NEXT: @available(*, unavailable, message: "constructors of abstract C++ classes are unavailable in Swift")
5+
// CHECK-NEXT: init()
56
// CHECK-NEXT: @available(*, unavailable, message: "virtual function is not available in Swift because it is pure")
67
// CHECK-NEXT: mutating func foo()
8+
// CHECK: }
9+
10+
// CHECK: struct Base3 {
11+
// CHECK-NEXT: init()
12+
// CHECK: }
13+
14+
// CHECK: struct Derived2 {
15+
// CHECK-NEXT: init()
16+
// CHECK: }
717

818
// CHECK: struct Derived<CInt> {
919
// CHECK-NEXT: init()

test/Interop/Cxx/class/inheritance/virtual-methods-typechecker.swift

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,13 @@
22

33
import VirtualMethods
44

5+
let _ = Base() // expected-error {{'init()' is unavailable: constructors of abstract C++ classes are unavailable in Swift}}
6+
let _ = Base2() // expected-error {{'init()' is unavailable: constructors of abstract C++ classes are unavailable in Swift}}
7+
8+
let _ = DerivedInt()
9+
let _ = Derived2()
10+
let _ = Derived3()
11+
let _ = Derived4()
12+
let _ = DerivedFromDerived2()
13+
514
VirtualNonAbstractBase().nonAbstractMethod()

0 commit comments

Comments
 (0)