Skip to content

Commit f64b7c4

Browse files
authored
Merge pull request #35819 from zoecarver/cxx/fix-full-inst
[cxx-interop] Fix class template instantiation.
2 parents 0836707 + b280d26 commit f64b7c4

9 files changed

+121
-16
lines changed

lib/ClangImporter/ImportDecl.cpp

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3738,13 +3738,6 @@ namespace {
37383738
if (isSpecializationDepthGreaterThan(def, 8))
37393739
return nullptr;
37403740

3741-
// FIXME: This will instantiate all members of the specialization (and detect
3742-
// instantiation failures in them), which can be more than is necessary
3743-
// and is more than what Clang does. As a result we reject some C++
3744-
// programs that Clang accepts.
3745-
Impl.getClangSema().InstantiateClassTemplateSpecializationMembers(
3746-
def->getLocation(), def, clang::TSK_ExplicitInstantiationDefinition);
3747-
37483741
return VisitCXXRecordDecl(def);
37493742
}
37503743

lib/IRGen/GenClangDecl.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
//===----------------------------------------------------------------------===//
1212

1313
#include "IRGenModule.h"
14+
#include "swift/AST/ASTContext.h"
1415
#include "swift/AST/IRGenOptions.h"
1516
#include "clang/AST/Decl.h"
1617
#include "clang/AST/DeclCXX.h"
@@ -20,6 +21,7 @@
2021
#include "clang/AST/GlobalDecl.h"
2122
#include "clang/AST/RecursiveASTVisitor.h"
2223
#include "clang/CodeGen/ModuleBuilder.h"
24+
#include "clang/Sema/Sema.h"
2325
#include "llvm/ADT/SmallPtrSet.h"
2426

2527
using namespace swift;
@@ -131,6 +133,15 @@ void IRGenModule::emitClangDecl(const clang::Decl *decl) {
131133
if (isa<clang::FieldDecl>(next)) {
132134
continue;
133135
}
136+
// If a method calls another method in a class template specialization, we
137+
// need to instantiate that other method. Do that here.
138+
if (auto *method = dyn_cast<clang::CXXMethodDecl>(next)) {
139+
// Make sure that this method is part of a class template specialization.
140+
if (method->getTemplateInstantiationPattern())
141+
Context.getClangModuleLoader()
142+
->getClangSema()
143+
.InstantiateFunctionDefinition(method->getLocation(), method);
144+
}
134145
ClangCodeGen->HandleTopLevelDecl(clang::DeclGroupRef(next));
135146
}
136147
}

lib/Sema/CSApply.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,21 @@ Solution::resolveConcreteDeclRef(ValueDecl *decl,
181181
auto sig = decl->getInnermostDeclContext()->getGenericSignatureOfContext();
182182
auto subst = computeSubstitutions(sig, locator);
183183

184+
// Lazily instantiate function definitions for class template specializations.
185+
// Members of a class template specialization will be instantiated here (not
186+
// when imported). If this method has already be instantiated, then this is a
187+
// no-op.
188+
if (const auto *constMethod =
189+
dyn_cast_or_null<clang::CXXMethodDecl>(decl->getClangDecl())) {
190+
auto method = const_cast<clang::CXXMethodDecl *>(constMethod);
191+
// Make sure that this method is part of a class template specialization.
192+
if (method->getTemplateInstantiationPattern())
193+
decl->getASTContext()
194+
.getClangModuleLoader()
195+
->getClangSema()
196+
.InstantiateFunctionDefinition(method->getLocation(), method);
197+
}
198+
184199
// If this is a C++ function template, get it's specialization for the given
185200
// substitution map and update the decl accordingly.
186201
if (decl->getClangDecl() &&

test/Interop/Cxx/templates/Inputs/class-template-instantiation-errors.h

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,21 @@ struct IntWrapper {
1414

1515
template<class T>
1616
struct CannotBeInstantianted {
17-
void willFailInstantiating(T t) {
18-
t.doesNotExist();
19-
}
17+
T value;
18+
19+
CannotBeInstantianted(char, T value) { value.doesNotExist(); }
20+
CannotBeInstantianted(char, char) { memberWrongType(); }
21+
CannotBeInstantianted(T value) : value(value) {}
22+
23+
void callsMethodWithError() { memberWrongType(); }
24+
25+
void memberWrongType() { value.doesNotExist(); }
26+
27+
void argWrongType(T t) { t.doesNotExist(); }
28+
29+
int getOne() { return 1; }
30+
int incValue() { return value.value + getOne(); }
31+
int incValue(T t) { return t.value + getOne(); }
2032
};
2133

2234
#endif // TEST_INTEROP_CXX_TEMPLATES_INPUTS_CLASS_TEMPLATE_INSTANTIATION_ERRORS_H
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// RUN: not %target-swift-emit-ir %s -I %S/Inputs -enable-cxx-interop 2>&1 | %FileCheck %s
2+
3+
import ClassTemplateInstantiationErrors
4+
5+
// CHECK: error: no member named 'doesNotExist' in 'IntWrapper'
6+
// CHECK: note: in instantiation of member function 'CannotBeInstantianted<IntWrapper>::memberWrongType' requested here
7+
public func test() {
8+
var y = CannotBeInstantianted<IntWrapper>(IntWrapper())
9+
y.callsMethodWithError()
10+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// RUN: not %target-swift-emit-ir %s -I %S/Inputs -enable-cxx-interop 2>&1 | %FileCheck %s
2+
3+
import ClassTemplateInstantiationErrors
4+
5+
// CHECK: error: no member named 'doesNotExist' in 'IntWrapper'
6+
// CHECK: note: in instantiation of member function 'CannotBeInstantianted<IntWrapper>::memberWrongType' requested here
7+
public func test() {
8+
var x = CannotBeInstantianted<IntWrapper>(CChar(0), CChar(0))
9+
x.incValue() // This is just to make sure "x" isn't removed.
10+
}
Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: not %target-swift-emit-sil %s -I %S/Inputs -enable-cxx-interop 2>&1 | %FileCheck %s
1+
// RUN: not %target-swift-emit-ir %s -I %S/Inputs -enable-cxx-interop 2>&1 | %FileCheck %s
22

33
import ClassTemplateInstantiationErrors
44

@@ -7,8 +7,17 @@ func swiftTemplateArgNotSupported() {
77
var _ = MagicWrapper<Optional>(t: "asdf")
88
}
99

10-
// CHECK: class-template-instantiation-errors.h:18:7: error: no member named 'doesNotExist' in 'IntWrapper'
11-
// CHECK: class-template-instantiation-errors.h:16:8: note: in instantiation of member function 'CannotBeInstantianted<IntWrapper>::willFailInstantiating' requested here
12-
func clangErrorReportedOnInstantiation() {
13-
var _ = CannotBeInstantianted<IntWrapper>()
14-
}
10+
// CHECK: error: no member named 'doesNotExist' in 'IntWrapper'
11+
// CHECK: note: in instantiation of member function 'CannotBeInstantianted<IntWrapper>::CannotBeInstantianted' requested here
12+
13+
// CHECK: error: no member named 'doesNotExist' in 'IntWrapper'
14+
// CHECK: note: in instantiation of member function 'CannotBeInstantianted<IntWrapper>::memberWrongType' requested here
15+
16+
// CHECK: error: no member named 'doesNotExist' in 'IntWrapper'
17+
// CHECK: note: in instantiation of member function 'CannotBeInstantianted<IntWrapper>::argWrongType' requested here
18+
public func clangErrorReportedOnInstantiation() {
19+
_ = CannotBeInstantianted<IntWrapper>(CChar(0), IntWrapper())
20+
var z = CannotBeInstantianted<IntWrapper>(IntWrapper())
21+
z.memberWrongType()
22+
z.argWrongType(IntWrapper())
23+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// RUN: %target-swift-emit-ir %s -I %S/Inputs -enable-cxx-interop | %FileCheck %s
2+
3+
import ClassTemplateInstantiationErrors
4+
5+
// CHECK-LABEL: define {{.*}}void @"$s4main23instantiateValidMembersyyF"()
6+
// CHECK: call i32 @{{_ZN21CannotBeInstantiantedI10IntWrapperE8incValueEv|"\?incValue@\?\$CannotBeInstantianted@UIntWrapper@@@@QEAAHXZ"}}(%struct.CannotBeInstantianted*
7+
// CHECK: call i32 @{{_ZN21CannotBeInstantiantedI10IntWrapperE8incValueES0_|"\?incValue@\?\$CannotBeInstantianted@UIntWrapper@@@@QEAAHUIntWrapper@@@Z"}}(%struct.CannotBeInstantianted* {{.*}}, {{i32|i64|\[1 x i32\]}}
8+
// CHECK: ret void
9+
10+
// CHECK-LABEL: define {{.*}}i32 @{{_ZN21CannotBeInstantiantedI10IntWrapperE8incValueEv|"\?incValue@\?\$CannotBeInstantianted@UIntWrapper@@@@QEAAHXZ"}}(%struct.CannotBeInstantianted*
11+
// CHECK: call i32 @{{_ZN21CannotBeInstantiantedI10IntWrapperE6getOneEv|"\?getOne@\?\$CannotBeInstantianted@UIntWrapper@@@@QEAAHXZ"}}(%struct.CannotBeInstantianted*
12+
// CHECK: ret i32
13+
14+
// CHECK-LABEL: define {{.*}}i32 @{{_ZN21CannotBeInstantiantedI10IntWrapperE8incValueES0_|"\?incValue@\?\$CannotBeInstantianted@UIntWrapper@@@@QEAAHUIntWrapper@@@Z"}}(%struct.CannotBeInstantianted* {{.*}}, {{i32|i64|\[1 x i32\]}}
15+
// CHECK: call i32 @{{_ZN21CannotBeInstantiantedI10IntWrapperE6getOneEv|"\?getOne@\?\$CannotBeInstantianted@UIntWrapper@@@@QEAAHXZ"}}(%struct.CannotBeInstantianted*
16+
// CHECK: ret i32
17+
18+
// CHECK-LABEL: define {{.*}}i32 @{{_ZN21CannotBeInstantiantedI10IntWrapperE6getOneEv|"\?getOne@\?\$CannotBeInstantianted@UIntWrapper@@@@QEAAHXZ"}}(%struct.CannotBeInstantianted*
19+
// CHECK: ret i32 1
20+
public func instantiateValidMembers() {
21+
var x = CannotBeInstantianted<IntWrapper>(IntWrapper(value: 41))
22+
x.incValue()
23+
var y = CannotBeInstantianted<IntWrapper>(IntWrapper(value: 0))
24+
y.incValue(IntWrapper(value: 41))
25+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// RUN: %target-run-simple-swift(-I %S/Inputs -Xfrontend -enable-cxx-interop)
2+
//
3+
// REQUIRES: executable_test
4+
5+
import ClassTemplateInstantiationErrors
6+
import StdlibUnittest
7+
8+
var TemplatesTestSuite = TestSuite("Template with uninstantiatable members")
9+
10+
TemplatesTestSuite.test("Calls valid member") {
11+
var x = CannotBeInstantianted<IntWrapper>(IntWrapper(value: 41))
12+
expectEqual(x.incValue(), 42)
13+
}
14+
15+
TemplatesTestSuite.test("Calls valid member on arg") {
16+
var x = CannotBeInstantianted<IntWrapper>(IntWrapper(value: 0))
17+
expectEqual(x.incValue(IntWrapper(value: 41)), 42)
18+
}
19+
20+
runAllTests()

0 commit comments

Comments
 (0)