Skip to content

Commit b60dcc1

Browse files
authored
Merge pull request #41394 from zoecarver/revert-revert-cleanup-and-fixes-for-function-templates
2 parents 8166ad4 + e27ea33 commit b60dcc1

13 files changed

+182
-158
lines changed

lib/ClangImporter/ClangImporter.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4672,8 +4672,7 @@ clang::FunctionDecl *ClangImporter::instantiateCXXFunctionTemplate(
46724672
ctx.Diags.diagnose(SourceLoc(),
46734673
diag::unable_to_convert_generic_swift_types.ID,
46744674
{func->getName(), StringRef(failedTypesStr)});
4675-
// Return a valid FunctionDecl but, we'll never use it.
4676-
return func->getAsFunction();
4675+
return nullptr;
46774676
}
46784677

46794678
// Instanciate a specialization of this template using the substitution map.

lib/ClangImporter/ImportType.cpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1945,7 +1945,7 @@ ParameterList *ClangImporter::Implementation::importFunctionParameterList(
19451945
Type swiftParamTy;
19461946
bool isParamTypeImplicitlyUnwrapped = false;
19471947
bool isInOut = false;
1948-
if ((isa<clang::ReferenceType>(paramTy) || isa<clang::PointerType>(paramTy)) &&
1948+
if (isa<clang::PointerType>(paramTy) &&
19491949
isa<clang::TemplateTypeParmType>(paramTy->getPointeeType())) {
19501950
auto pointeeType = paramTy->getPointeeType();
19511951
auto templateParamType = cast<clang::TemplateTypeParmType>(pointeeType);
@@ -1957,6 +1957,13 @@ ParameterList *ClangImporter::Implementation::importFunctionParameterList(
19571957
swiftParamTy = genericType->wrapInPointer(pointerKind);
19581958
if (!swiftParamTy)
19591959
return nullptr;
1960+
} else if (isa<clang::ReferenceType>(paramTy) &&
1961+
isa<clang::TemplateTypeParmType>(paramTy->getPointeeType())) {
1962+
auto templateParamType =
1963+
cast<clang::TemplateTypeParmType>(paramTy->getPointeeType());
1964+
swiftParamTy =
1965+
findGenericTypeInGenericDecls(templateParamType, genericParams);
1966+
isInOut = true;
19601967
} else if (auto *templateParamType =
19611968
dyn_cast<clang::TemplateTypeParmType>(paramTy)) {
19621969
swiftParamTy =

lib/Sema/CSApply.cpp

Lines changed: 147 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,76 @@ Solution::computeSubstitutions(GenericSignature sig,
109109
lookupConformanceFn);
110110
}
111111

112+
// On Windows and 32-bit platforms we need to force "Int" to actually be
113+
// re-imported as "Int." This is needed because otherwise, we cannot round-trip
114+
// "Int" and "UInt". For example, on Windows, "Int" will be imported into C++ as
115+
// "long long" and then back into Swift as "Int64" not "Int."
116+
static ValueDecl *rewriteIntegerTypes(SubstitutionMap subst, ValueDecl *oldDecl,
117+
AbstractFunctionDecl *newDecl) {
118+
auto originalFnSubst = cast<AbstractFunctionDecl>(oldDecl)
119+
->getInterfaceType()
120+
->getAs<GenericFunctionType>()
121+
->substGenericArgs(subst);
122+
// The constructor type is a function type as follows:
123+
// (CType.Type) -> (Generic) -> CType
124+
// And a method's function type is as follows:
125+
// (inout CType) -> (Generic) -> Void
126+
// In either case, we only want the result of that function type because that
127+
// is the function type with the generic params that need to be substituted:
128+
// (Generic) -> CType
129+
if (isa<ConstructorDecl>(oldDecl) || oldDecl->isInstanceMember() ||
130+
oldDecl->isStatic())
131+
originalFnSubst = cast<FunctionType>(originalFnSubst->getResult().getPointer());
132+
133+
SmallVector<ParamDecl *, 4> fixedParameters;
134+
unsigned parameterIndex = 0;
135+
for (auto *newFnParam : *newDecl->getParameters()) {
136+
// If the user substituted this param with an (U)Int, use (U)Int.
137+
auto substParamType =
138+
originalFnSubst->getParams()[parameterIndex].getParameterType();
139+
if (substParamType->isEqual(newDecl->getASTContext().getIntType()) ||
140+
substParamType->isEqual(newDecl->getASTContext().getUIntType())) {
141+
auto intParam =
142+
ParamDecl::cloneWithoutType(newDecl->getASTContext(), newFnParam);
143+
intParam->setInterfaceType(substParamType);
144+
fixedParameters.push_back(intParam);
145+
} else {
146+
fixedParameters.push_back(newFnParam);
147+
}
148+
parameterIndex++;
149+
}
150+
151+
auto fixedParams =
152+
ParameterList::create(newDecl->getASTContext(), fixedParameters);
153+
newDecl->setParameters(fixedParams);
154+
155+
// Now fix the result type:
156+
if (originalFnSubst->getResult()->isEqual(
157+
newDecl->getASTContext().getIntType()) ||
158+
originalFnSubst->getResult()->isEqual(
159+
newDecl->getASTContext().getUIntType())) {
160+
// Constructors don't have a result.
161+
if (auto func = dyn_cast<FuncDecl>(newDecl)) {
162+
// We have to rebuild the whole function.
163+
auto newFnDecl = FuncDecl::createImported(
164+
func->getASTContext(), func->getNameLoc(),
165+
func->getName(), func->getNameLoc(),
166+
func->hasAsync(), func->hasThrows(),
167+
fixedParams, originalFnSubst->getResult(),
168+
/*genericParams=*/nullptr, func->getDeclContext(), newDecl->getClangDecl());
169+
if (func->isStatic()) newFnDecl->setStatic();
170+
if (func->isImportAsStaticMember()) newFnDecl->setImportAsStaticMember();
171+
if (func->getImportAsMemberStatus().isInstance()) {
172+
newFnDecl->setSelfAccessKind(func->getSelfAccessKind());
173+
newFnDecl->setSelfIndex(func->getSelfIndex());
174+
}
175+
return newFnDecl;
176+
}
177+
}
178+
179+
return newDecl;
180+
}
181+
112182
// Derive a concrete function type for fdecl by substituting the generic args
113183
// and use that to derive the corresponding function type and parameter list.
114184
static std::pair<FunctionType *, ParameterList *>
@@ -149,65 +219,6 @@ substituteFunctionTypeAndParamList(ASTContext &ctx, AbstractFunctionDecl *fdecl,
149219
return {newFnType, newParamList};
150220
}
151221

152-
static ValueDecl *generateSpecializedCXXFunctionTemplate(
153-
ASTContext &ctx, AbstractFunctionDecl *oldDecl, SubstitutionMap subst,
154-
clang::FunctionDecl *specialized) {
155-
auto newFnTypeAndParams = substituteFunctionTypeAndParamList(ctx, oldDecl, subst);
156-
auto newFnType = newFnTypeAndParams.first;
157-
auto paramList = newFnTypeAndParams.second;
158-
159-
SmallVector<ParamDecl *, 4> newParamsWithoutMetatypes;
160-
for (auto param : *paramList ) {
161-
if (isa<FuncDecl>(oldDecl) &&
162-
isa<MetatypeType>(param->getType().getPointer())) {
163-
// Metatype parameters are added synthetically to account for template
164-
// params that don't make it to the function signature. These shouldn't
165-
// exist in the resulting specialized FuncDecl. Note that this doesn't
166-
// affect constructors because all template params for a constructor
167-
// must be in the function signature by design.
168-
continue;
169-
}
170-
newParamsWithoutMetatypes.push_back(param);
171-
}
172-
auto *newParamList =
173-
ParameterList::create(ctx, SourceLoc(), newParamsWithoutMetatypes, SourceLoc());
174-
175-
if (isa<ConstructorDecl>(oldDecl)) {
176-
DeclName ctorName(ctx, DeclBaseName::createConstructor(), newParamList);
177-
auto newCtorDecl = ConstructorDecl::createImported(
178-
ctx, specialized, ctorName, oldDecl->getLoc(),
179-
/*failable=*/false, /*failabilityLoc=*/SourceLoc(),
180-
/*Async=*/false, /*AsyncLoc=*/SourceLoc(),
181-
/*throws=*/false, /*throwsLoc=*/SourceLoc(),
182-
newParamList, /*genericParams=*/nullptr,
183-
oldDecl->getDeclContext());
184-
return newCtorDecl;
185-
}
186-
187-
// Generate a name for the specialized function.
188-
std::string newNameStr;
189-
llvm::raw_string_ostream buffer(newNameStr);
190-
std::unique_ptr<clang::MangleContext> mangler(
191-
specialized->getASTContext().createMangleContext());
192-
mangler->mangleName(specialized, buffer);
193-
buffer.flush();
194-
// Add all parameters as empty parameters.
195-
auto newName = DeclName(
196-
ctx, DeclName(ctx.getIdentifier(newNameStr)).getBaseName(), newParamList);
197-
198-
auto newFnDecl = FuncDecl::createImported(
199-
ctx, oldDecl->getLoc(), newName, oldDecl->getNameLoc(),
200-
/*Async=*/false, oldDecl->hasThrows(), newParamList,
201-
newFnType->getResult(), /*GenericParams=*/nullptr,
202-
oldDecl->getDeclContext(), specialized);
203-
if (oldDecl->isStatic()) {
204-
newFnDecl->setStatic();
205-
newFnDecl->setImportAsStaticMember();
206-
}
207-
newFnDecl->setSelfAccessKind(cast<FuncDecl>(oldDecl)->getSelfAccessKind());
208-
return newFnDecl;
209-
}
210-
211222
// Synthesizes the body of a thunk that takes extra metatype arguments and
212223
// skips over them to forward them along to the FuncDecl contained by context.
213224
// This is used when importing a C++ templated function where the template params
@@ -249,20 +260,32 @@ synthesizeForwardingThunkBody(AbstractFunctionDecl *afd, void *context) {
249260
return {body, /*isTypeChecked=*/true};
250261
}
251262

252-
ConcreteDeclRef
253-
Solution::resolveConcreteDeclRef(ValueDecl *decl,
254-
ConstraintLocator *locator) const {
255-
if (!decl)
256-
return ConcreteDeclRef();
257-
258-
// Get the generic signatue of the decl and compute the substitutions.
259-
auto sig = decl->getInnermostDeclContext()->getGenericSignatureOfContext();
260-
auto subst = computeSubstitutions(sig, locator);
263+
static ValueDecl *generateThunkForExtraMetatypes(SubstitutionMap subst,
264+
FuncDecl *oldDecl,
265+
FuncDecl *newDecl) {
266+
// We added additional metatype parameters to aid template
267+
// specialization, which are no longer now that we've specialized
268+
// this function. Create a thunk that only forwards the original
269+
// parameters along to the clang function.
270+
auto thunkTypeAndParamList = substituteFunctionTypeAndParamList(oldDecl->getASTContext(),
271+
oldDecl, subst);
272+
auto thunk = FuncDecl::createImplicit(
273+
oldDecl->getASTContext(), oldDecl->getStaticSpelling(), oldDecl->getName(),
274+
oldDecl->getNameLoc(), oldDecl->hasAsync(), oldDecl->hasThrows(),
275+
/*genericParams=*/nullptr, thunkTypeAndParamList.second,
276+
thunkTypeAndParamList.first->getResult(), oldDecl->getDeclContext());
277+
thunk->copyFormalAccessFrom(oldDecl);
278+
thunk->setBodySynthesizer(synthesizeForwardingThunkBody, newDecl);
279+
thunk->setSelfAccessKind(oldDecl->getSelfAccessKind());
280+
281+
return thunk;
282+
}
261283

262-
// Lazily instantiate function definitions for class template specializations.
263-
// Members of a class template specialization will be instantiated here (not
264-
// when imported). If this method has already be instantiated, then this is a
265-
// no-op.
284+
// Lazily instantiate function definitions for class template specializations.
285+
// Members of a class template specialization will be instantiated here (not
286+
// when imported). If this method has already be instantiated, then this is a
287+
// no-op.
288+
static void maybeInstantiateCXXMethodDefinition(ValueDecl *decl) {
266289
if (const auto *constMethod =
267290
dyn_cast_or_null<clang::CXXMethodDecl>(decl->getClangDecl())) {
268291
auto method = const_cast<clang::CXXMethodDecl *>(constMethod);
@@ -273,41 +296,62 @@ Solution::resolveConcreteDeclRef(ValueDecl *decl,
273296
->getClangSema()
274297
.InstantiateFunctionDefinition(method->getLocation(), method);
275298
}
299+
}
300+
301+
static ConcreteDeclRef getCXXFunctionTemplateSpecialization(SubstitutionMap subst,
302+
ValueDecl *decl) {
303+
assert(isa<clang::FunctionTemplateDecl>(decl->getClangDecl()) &&
304+
"This API should only be used with function templates.");
305+
306+
auto *newFn =
307+
decl->getASTContext()
308+
.getClangModuleLoader()
309+
->instantiateCXXFunctionTemplate(
310+
decl->getASTContext(),
311+
const_cast<clang::FunctionTemplateDecl *>(
312+
cast<clang::FunctionTemplateDecl>(decl->getClangDecl())),
313+
subst);
314+
// We failed to specialize this function template. The compiler is going to
315+
// exit soon. Return something valid in the meantime.
316+
if (!newFn)
317+
return ConcreteDeclRef(decl);
318+
319+
auto newDecl = cast_or_null<ValueDecl>(
320+
decl->getASTContext().getClangModuleLoader()->importDeclDirectly(
321+
newFn));
322+
323+
if (auto fn = dyn_cast<AbstractFunctionDecl>(newDecl)) {
324+
if (!subst.empty()) {
325+
newDecl = rewriteIntegerTypes(subst, decl, fn);
326+
}
327+
}
328+
329+
if (auto fn = dyn_cast<FuncDecl>(decl)) {
330+
if (newFn->getNumParams() != fn->getParameters()->size()) {
331+
newDecl = generateThunkForExtraMetatypes(subst, fn,
332+
cast<FuncDecl>(newDecl));
333+
}
334+
}
335+
336+
return ConcreteDeclRef(newDecl);
337+
}
338+
339+
ConcreteDeclRef
340+
Solution::resolveConcreteDeclRef(ValueDecl *decl,
341+
ConstraintLocator *locator) const {
342+
if (!decl)
343+
return ConcreteDeclRef();
344+
345+
// Get the generic signatue of the decl and compute the substitutions.
346+
auto sig = decl->getInnermostDeclContext()->getGenericSignatureOfContext();
347+
auto subst = computeSubstitutions(sig, locator);
348+
349+
maybeInstantiateCXXMethodDefinition(decl);
276350

277351
// If this is a C++ function template, get it's specialization for the given
278352
// substitution map and update the decl accordingly.
279353
if (isa_and_nonnull<clang::FunctionTemplateDecl>(decl->getClangDecl())) {
280-
auto *newFn =
281-
decl->getASTContext()
282-
.getClangModuleLoader()
283-
->instantiateCXXFunctionTemplate(
284-
decl->getASTContext(),
285-
const_cast<clang::FunctionTemplateDecl *>(
286-
cast<clang::FunctionTemplateDecl>(decl->getClangDecl())),
287-
subst);
288-
auto newDecl = generateSpecializedCXXFunctionTemplate(
289-
decl->getASTContext(), cast<AbstractFunctionDecl>(decl), subst, newFn);
290-
if (auto fn = dyn_cast<FuncDecl>(decl)) {
291-
if (newFn->getNumParams() != fn->getParameters()->size()) {
292-
// We added additional metatype parameters to aid template
293-
// specialization, which are no longer now that we've specialized
294-
// this function. Create a thunk that only forwards the original
295-
// parameters along to the clang function.
296-
auto thunkTypeAndParamList = substituteFunctionTypeAndParamList(decl->getASTContext(),
297-
fn, subst);
298-
auto thunk = FuncDecl::createImplicit(
299-
fn->getASTContext(), fn->getStaticSpelling(), fn->getName(),
300-
fn->getNameLoc(), fn->hasAsync(), fn->hasThrows(),
301-
/*genericParams=*/nullptr, thunkTypeAndParamList.second,
302-
thunkTypeAndParamList.first->getResult(), fn->getDeclContext());
303-
thunk->copyFormalAccessFrom(fn);
304-
thunk->setBodySynthesizer(synthesizeForwardingThunkBody, cast<FuncDecl>(newDecl));
305-
thunk->setSelfAccessKind(fn->getSelfAccessKind());
306-
307-
return ConcreteDeclRef(thunk);
308-
}
309-
}
310-
return ConcreteDeclRef(newDecl);
354+
return getCXXFunctionTemplateSpecialization(subst, decl);
311355
}
312356

313357
return ConcreteDeclRef(decl, subst);

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ typealias Void = ()
1111
struct UnsafePointer<T> { }
1212
struct UnsafeMutablePointer<T> { }
1313

14+
struct Int { }
15+
struct UInt { }
16+
1417
public func createHasVirtualBase() -> HasVirtualBase {
1518
// ITANIUM_X64: define swiftcc void @"$ss20createHasVirtualBaseSo0bcD0VyF"(%TSo14HasVirtualBaseV* noalias nocapture sret({{.*}}) %0)
1619
// ITANIUM_X64-NOT: define

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ public func deletedConstructor(a: UnsafeMutablePointer<Int32>) {
6565
// CHECK: apply [[FN]]([[TEMPL]], [[ARG_VAL]]) : $@convention(c) (ArgType) -> @out TemplatedConstructor
6666
// CHECK-LABEL: end sil function '$s4main20templatedConstructoryyF'
6767

68-
// CHECK-LABEL: sil hidden_external [clang TemplatedConstructor.init] @{{_ZN20TemplatedConstructorC1I7ArgTypeEET_|\?\?\$\?0UArgType@@@TemplatedConstructor@@QEAA@UArgType@@@Z}} : $@convention(c) (ArgType) -> @out TemplatedConstructor
68+
// CHECK-LABEL: sil [clang TemplatedConstructor.init] @{{_ZN20TemplatedConstructorC1I7ArgTypeEET_|\?\?\$\?0UArgType@@@TemplatedConstructor@@QEAA@UArgType@@@Z}} : $@convention(c) (ArgType) -> @out TemplatedConstructor
6969
public func templatedConstructor() {
7070
let templated = TemplatedConstructor(ArgType())
7171
}

test/Interop/Cxx/templates/defaulted-template-type-parameter-module-interface.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
// CHECK: func defaultedTemplateTypeParamAndUnrealtedParam(_: Int32)
1515
// CHECK: func overloadedDefaultedTemplate<T>(_: T)
1616
// CHECK: func overloadedDefaultedTemplate(_: Int32)
17-
// CHECK: func defaultedTemplateReferenceTypeParam<T>(_ t: UnsafeMutablePointer<T>)
17+
// CHECK: func defaultedTemplateReferenceTypeParam<T>(_ t: inout T)
1818
// The following types aren't imported correctly, but that does not have to do
1919
// with the fact that the template type paramaters are defaulted.
2020
// CHECK: func defaultedTemplatePointerTypeParam<T>(_ t: UnsafeMutablePointer<T>)

test/Interop/Cxx/templates/function-template-irgen-objc.swift

Lines changed: 0 additions & 17 deletions
This file was deleted.

test/Interop/Cxx/templates/function-template-module-interface.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,13 @@
1414
// CHECK: mutating func test2(_: Int32, _ varargs: Any...)
1515
// CHECK: }
1616

17-
// CHECK: func lvalueReference<T>(_ ref: UnsafeMutablePointer<T>)
18-
// CHECK: func constLvalueReference<T>(_: UnsafePointer<T>)
19-
// CHECK: func forwardingReference<T>(_: UnsafeMutablePointer<T>)
17+
// CHECK: func lvalueReference<T>(_ ref: inout T)
18+
// CHECK: func constLvalueReference<T>(_: inout T)
19+
// CHECK: func forwardingReference<T>(_: inout T)
2020
// CHECK: func PointerTemplateParameter<T>(_: UnsafeMutablePointer<T>)
2121

2222
// CHECK: enum Orbiters {
2323
// CHECK: static func galileo<T>(_: T)
2424
// CHECK: static func cassini<T, U>(_: T, _: U)
25-
// CHECK: static func magellan<T>(_: UnsafeMutablePointer<T>)
25+
// CHECK: static func magellan<T>(_: inout T)
2626
// CHECK: }

test/Interop/Cxx/templates/function-template.swift

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -28,15 +28,6 @@ FunctionTemplateTestSuite.test("lvalueReference<T> where T == Int") {
2828
expectEqual(value, 42)
2929
}
3030

31-
// TODO: currently "Any" is imported as an Objective-C "id".
32-
// This doesn't work without the Objective-C runtime.
33-
#if _runtime(_ObjC)
34-
FunctionTemplateTestSuite.test("passThrough<T> where T == Any") {
35-
let result = passThrough(42 as Any)
36-
expectEqual(42, result as! Int)
37-
}
38-
#endif
39-
4031
// TODO: Generics, Any, and Protocols should be tested here but need to be
4132
// better supported in ClangTypeConverter first.
4233

0 commit comments

Comments
 (0)