Skip to content

Commit 111a028

Browse files
committed
[cxx-interop] Import nullability of templated function parameters correctly
This teaches ClangImporter to respect the `_Nonnull`/`_Nullable` arguments on templated function parameters. Previously Swift would only import a non-annotated function overload. Using an overload that has either `_Nonnull` or `_Nullable` would result in a compiler error. The non-annotated overload would get imported with incorrect nullability: Swift would always assume non-null pointers, which was inconsistent with non-templated function parameters, which are mapped to implicitly unwrapped optionals. With this change all three possible overloads are imported, and all of them get the correct nullability in Swift. rdar://151939344
1 parent 1d4e9d0 commit 111a028

File tree

4 files changed

+45
-4
lines changed

4 files changed

+45
-4
lines changed

lib/ClangImporter/ImportType.cpp

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2455,6 +2455,13 @@ ClangImporter::Implementation::importParameterType(
24552455
llvm::function_ref<void(Diagnostic &&)> addImportDiagnosticFn) {
24562456
auto paramTy = desugarIfElaborated(param->getType());
24572457

2458+
// If this type has a _Nullable/_Nonnull attribute, drop it, since we already
2459+
// have that information in optionalityOfParam.
2460+
if (auto attributedTy = dyn_cast<clang::AttributedType>(paramTy)) {
2461+
if (attributedTy->getImmediateNullability())
2462+
clang::AttributedType::stripOuterNullability(paramTy);
2463+
}
2464+
24582465
ImportTypeKind importKind = paramIsCompletionHandler
24592466
? ImportTypeKind::CompletionHandlerParameter
24602467
: ImportTypeKind::Parameter;
@@ -2483,6 +2490,17 @@ ClangImporter::Implementation::importParameterType(
24832490
pointerKind));
24842491
return std::nullopt;
24852492
}
2493+
switch (optionalityOfParam) {
2494+
case OTK_Optional:
2495+
swiftParamTy = OptionalType::get(swiftParamTy);
2496+
break;
2497+
case OTK_ImplicitlyUnwrappedOptional:
2498+
swiftParamTy = OptionalType::get(swiftParamTy);
2499+
isParamTypeImplicitlyUnwrapped = true;
2500+
break;
2501+
case OTK_None:
2502+
break;
2503+
}
24862504
} else if (isa<clang::ReferenceType>(paramTy) &&
24872505
isa<clang::TemplateTypeParmType>(paramTy->getPointeeType())) {
24882506
// We don't support universal reference, bail.
@@ -2731,8 +2749,6 @@ ParameterList *ClangImporter::Implementation::importFunctionParameterList(
27312749
}
27322750

27332751
bool knownNonNull = !nonNullArgs.empty() && nonNullArgs[index];
2734-
// Specialized templates need to match the args/result exactly.
2735-
knownNonNull |= clangDecl->isFunctionTemplateSpecialization();
27362752

27372753
// Check nullability of the parameter.
27382754
OptionalTypeKind optionalityOfParam =

test/Interop/Cxx/templates/Inputs/function-templates.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,9 @@ template <class T> bool constLvalueReferenceToBool(const T &t) { return t; }
120120

121121
template <class T> void forwardingReference(T &&) {}
122122

123-
template <class T> void PointerTemplateParameter(T*){}
123+
template <class T> bool pointerTemplateParameter(T *t) { return t; }
124+
template <class T> bool pointerTemplateParameterNonnull(T *_Nonnull t) { return t; }
125+
template <class T> bool pointerTemplateParameterNullable(T *_Nullable t) { return t; }
124126

125127
template <typename F> void callFunction(F f) { f(); }
126128
template <typename F, typename T> void callFunctionWithParam(F f, T t) { f(t); }

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,10 @@
2020

2121
// CHECK: func lvalueReference<T>(_ ref: inout T)
2222
// CHECK: func constLvalueReference<T>(_: T)
23-
// CHECK: func PointerTemplateParameter<T>(_: UnsafeMutablePointer<T>)
23+
24+
// CHECK: func pointerTemplateParameter<T>(_ t: UnsafeMutablePointer<T>!) -> Bool
25+
// CHECK: func pointerTemplateParameterNonnull<T>(_ t: UnsafeMutablePointer<T>) -> Bool
26+
// CHECK: func pointerTemplateParameterNullable<T>(_ t: UnsafeMutablePointer<T>?) -> Bool
2427

2528
// CHECK: enum Orbiters {
2629
// CHECK: static func galileo<T>(_: T)

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

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,26 @@ FunctionTemplateTestSuite.test("constLvalueReferenceToBool<T> where T == Bool")
3939
expectFalse(constLvalueReferenceToBool(false))
4040
}
4141

42+
var nilPtr: UnsafeMutablePointer<CInt>? = nil
43+
var nilPtrIOU: UnsafeMutablePointer<CInt>! = nil
44+
var nonNilPtr: UnsafeMutablePointer<CInt> = .init(bitPattern: 123)!
45+
46+
FunctionTemplateTestSuite.test("pointerTemplateParameter<T>") {
47+
expectFalse(pointerTemplateParameter(nilPtr))
48+
expectFalse(pointerTemplateParameter(nilPtrIOU))
49+
expectTrue(pointerTemplateParameter(nonNilPtr))
50+
}
51+
52+
FunctionTemplateTestSuite.test("pointerTemplateParameterNonnull<T>") {
53+
expectTrue(pointerTemplateParameterNonnull(nonNilPtr))
54+
}
55+
56+
FunctionTemplateTestSuite.test("pointerTemplateParameterNullable<T>") {
57+
expectFalse(pointerTemplateParameterNullable(nilPtr))
58+
expectFalse(pointerTemplateParameterNullable(nilPtrIOU))
59+
expectTrue(pointerTemplateParameterNullable(nonNilPtr))
60+
}
61+
4262
// TODO: Generics, Any, and Protocols should be tested here but need to be
4363
// better supported in ClangTypeConverter first.
4464

0 commit comments

Comments
 (0)