Skip to content

Commit f6e6172

Browse files
authored
Merge pull request swiftlang#82161 from swiftlang/egorzhdan/template-param-nullability
[cxx-interop] Import nullability of templated function parameters correctly
2 parents 304d558 + 607dd4a commit f6e6172

11 files changed

+71
-27
lines changed

lib/AST/ClangTypeConverter.cpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -890,8 +890,15 @@ ClangTypeConverter::convertClangDecl(Type type, const clang::Decl *clangDecl) {
890890

891891
if (auto clangTypeDecl = dyn_cast<clang::TypeDecl>(clangDecl)) {
892892
auto qualType = ctx.getTypeDeclType(clangTypeDecl);
893-
if (type->isForeignReferenceType())
893+
if (type->isForeignReferenceType()) {
894894
qualType = ctx.getPointerType(qualType);
895+
auto nonNullAttr = new (ctx) clang::TypeNonNullAttr(
896+
ctx,
897+
clang::AttributeCommonInfo(
898+
clang::SourceRange(), clang::AttributeCommonInfo::AT_TypeNonNull,
899+
clang::AttributeCommonInfo::Form::Implicit()));
900+
qualType = ctx.getAttributedType(nonNullAttr, qualType, qualType);
901+
}
895902

896903
return qualType.getUnqualifiedType();
897904
}

lib/ClangImporter/ImportType.cpp

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2458,6 +2458,13 @@ ClangImporter::Implementation::importParameterType(
24582458
auto paramTy = desugarIfElaborated(param->getType());
24592459
paramTy = desugarIfBoundsAttributed(paramTy);
24602460

2461+
// If this type has a _Nullable/_Nonnull attribute, drop it, since we already
2462+
// have that information in optionalityOfParam.
2463+
if (auto attributedTy = dyn_cast<clang::AttributedType>(paramTy)) {
2464+
if (attributedTy->getImmediateNullability())
2465+
clang::AttributedType::stripOuterNullability(paramTy);
2466+
}
2467+
24612468
ImportTypeKind importKind = paramIsCompletionHandler
24622469
? ImportTypeKind::CompletionHandlerParameter
24632470
: ImportTypeKind::Parameter;
@@ -2486,6 +2493,17 @@ ClangImporter::Implementation::importParameterType(
24862493
pointerKind));
24872494
return std::nullopt;
24882495
}
2496+
switch (optionalityOfParam) {
2497+
case OTK_Optional:
2498+
swiftParamTy = OptionalType::get(swiftParamTy);
2499+
break;
2500+
case OTK_ImplicitlyUnwrappedOptional:
2501+
swiftParamTy = OptionalType::get(swiftParamTy);
2502+
isParamTypeImplicitlyUnwrapped = true;
2503+
break;
2504+
case OTK_None:
2505+
break;
2506+
}
24892507
} else if (isa<clang::ReferenceType>(paramTy) &&
24902508
isa<clang::TemplateTypeParmType>(paramTy->getPointeeType())) {
24912509
// We don't support universal reference, bail.
@@ -2734,8 +2752,6 @@ ParameterList *ClangImporter::Implementation::importFunctionParameterList(
27342752
}
27352753

27362754
bool knownNonNull = !nonNullArgs.empty() && nonNullArgs[index];
2737-
// Specialized templates need to match the args/result exactly.
2738-
knownNonNull |= clangDecl->isFunctionTemplateSpecialization();
27392755

27402756
// Check nullability of the parameter.
27412757
OptionalTypeKind optionalityOfParam =

stdlib/public/Cxx/CxxSpan.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ public protocol CxxSpan<Element> {
6666
associatedtype Size: BinaryInteger
6767

6868
init()
69-
init(_ unsafePointer : UnsafePointer<Element>, _ count: Size)
69+
init(_ unsafePointer: UnsafePointer<Element>!, _ count: Size)
7070

7171
func size() -> Size
7272
func __dataUnsafe() -> UnsafePointer<Element>?
@@ -136,7 +136,7 @@ public protocol CxxMutableSpan<Element> {
136136
associatedtype Size: BinaryInteger
137137

138138
init()
139-
init(_ unsafeMutablePointer : UnsafeMutablePointer<Element>, _ count: Size)
139+
init(_ unsafeMutablePointer: UnsafeMutablePointer<Element>!, _ count: Size)
140140

141141
func size() -> Size
142142
func __dataUnsafe() -> UnsafeMutablePointer<Element>?
Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
11
template <class From, class To>
2-
To __swift_interopStaticCast(From from) { return static_cast<To>(from); }
2+
To _Nonnull __swift_interopStaticCast(From _Nonnull from) {
3+
return static_cast<To>(from);
4+
}

test/Interop/Cxx/stdlib/use-std-span-typechecker.swift

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,5 @@ arr.withUnsafeBufferPointer { ubpointer in
1515
arr.withUnsafeBufferPointer { ubpointer in
1616
// FIXME: this crashes the compiler once we import span's templated ctors as Swift generics.
1717
let _ = ConstSpanOfInt(ubpointer.baseAddress, ubpointer.count)
18-
// expected-error@-1 {{value of optional type 'UnsafePointer<Int32>?' must be unwrapped to a value of type 'UnsafePointer<Int32>'}}
19-
// expected-note@-2 {{coalesce using '??' to provide a default when the optional value contains 'nil'}}
20-
// expected-note@-3 {{force-unwrap using '!' to abort execution if the optional value contains 'nil'}}
18+
// expected-warning@-1 {{'init(_:_:)' is deprecated: use 'init(_:)' instead.}}
2119
}

test/Interop/Cxx/swiftify-import/bounds-attrs-in-template.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@
55
// RUN: %target-swift-frontend -plugin-path %swift-plugin-dir -I %t/Inputs -cxx-interoperability-mode=default -enable-experimental-feature SafeInteropWrappers %t/template.swift -dump-macro-expansions -emit-ir -o %t/out -verify
66
// RUN: %target-swift-ide-test -plugin-path %swift-plugin-dir -I %t/Inputs -cxx-interoperability-mode=default -enable-experimental-feature SafeInteropWrappers -print-module -module-to-print=Template -source-filename=x | %FileCheck %s
77

8-
// CHECK: func cb_template<T>(_ p: UnsafePointer<T>, _ size: Int{{.*}}) -> UnsafePointer<T>
9-
// CHECK: func eb_template<T>(_ p: UnsafePointer<T>, _ end: UnsafePointer<T>) -> UnsafePointer<T>
10-
// CHECK: func s_template<T>(_ p: UnsafePointer<T>) -> UnsafePointer<T>
11-
// CHECK: func ui_template<T>(_ p: UnsafePointer<T>) -> UnsafePointer<T>
8+
// CHECK: func cb_template<T>(_ p: UnsafePointer<T>!, _ size: Int{{.*}}) -> UnsafePointer<T>
9+
// CHECK: func eb_template<T>(_ p: UnsafePointer<T>!, _ end: UnsafePointer<T>!) -> UnsafePointer<T>
10+
// CHECK: func s_template<T>(_ p: UnsafePointer<T>!) -> UnsafePointer<T>
11+
// CHECK: func ui_template<T>(_ p: UnsafePointer<T>!) -> UnsafePointer<T>
1212

1313
//--- Inputs/module.modulemap
1414
module Template {

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/defaulted-template-type-parameter-module-interface.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,6 @@
2121
// TODO-CHECK: func defaultedTemplatePointerTypeParam<T>(_ t: UnsafeMutablePointer<T>)
2222
// TODO-CHECK: func defaultedTemplatePointerPointerTypeParam<T>(_ t: UnsafeMutablePointer<OpaquePointer?>!)
2323

24-
// CHECK: func defaultedTemplatePointerTypeParam<T>(_ t: UnsafeMutablePointer<T>)
24+
// CHECK: func defaultedTemplatePointerTypeParam<T>(_ t: UnsafeMutablePointer<T>!)
2525
// We don't support references to dependent types (rdar://89034440).
2626
// CHECK-NOT: defaultedTemplatePointerReferenceTypeParam

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

test/Interop/Cxx/templates/template-instantiation-irgen.swift

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -46,21 +46,20 @@ func takesPtrToStruct(x: UnsafePointer<PlainStruct>) { takesValue(x) }
4646
func takesPtrToClass(x: UnsafePointer<CxxClass>) { takesValue(x) }
4747
// CHECK: define {{.*}} void @{{.*}}takesPtrToClass{{.*}}
4848

49-
// FIXME: this crashes because this round-trips to UnsafePointer<FRT?>
50-
// func takesPtrToFRT(x: UnsafePointer<FRT>) { takesValue(x) }
49+
func takesPtrToFRT(x: UnsafePointer<FRT>) { takesValue(x) }
50+
// CHECK: define {{.*}} void @{{.*}}takesPtrToFRT{{.*}}
5151

5252
func takesMutPtrToStruct(x: UnsafeMutablePointer<PlainStruct>) { takesValue(x) }
5353
// CHECK: define {{.*}} void @{{.*}}takesMutPtrToStruct{{.*}}
5454

5555
func takesMutPtrToClass(x: UnsafeMutablePointer<CxxClass>) { takesValue(x) }
5656
// CHECK: define {{.*}} void @{{.*}}takesMutPtrToClass{{.*}}
5757

58-
// FIXME: this crashes because this round-trips to UnsafeMutablePointer<FRT?>
59-
// func takesMutPtrToFRT(x: UnsafeMutablePointer<FRT>) { takesValue(x) }
58+
func takesMutPtrToFRT(x: UnsafeMutablePointer<FRT>) { takesValue(x) }
59+
// CHECK: define {{.*}} void @{{.*}}takesMutPtrToFRT{{.*}}
6060

6161
func takesCPtr() {
62-
// FIXME: optional pointers are not yet supported but they should be; this crashes
63-
// takesValue(intPtr)
62+
takesValue(intPtr)
6463

6564
// It's fine if we dereference it, though
6665
takesValue(intPtr!)
@@ -92,9 +91,6 @@ func takesSwiftClosureTakingCxxClass() { takesValue({(x: CxxClass) in takesValue
9291
func takesTakesCxxClass() { takesValue(takesCxxClass) }
9392

9493
func takesSwiftClosureReturningFRT() { takesValue({() -> FRT in FRT()}) }
94+
func takesSwiftClosureTakingFRT() { takesValue({(x: FRT) in takesValue(x)}) }
9595

96-
// FIXME: this crashes due to pointer round-tripping
97-
// func takesSwiftClosureTakingFRT() { takesValue({(x: FRT) in takesValue(x)}) }
98-
99-
// FIXME: this crashes due to pointer round-tripping
100-
// func takesTakesFRT() { takesValue(takesFRT) }
96+
func takesTakesFRT() { takesValue(takesFRT) }

0 commit comments

Comments
 (0)