Skip to content

Commit 466ae71

Browse files
authored
Merge pull request #74494 from hyp/eng/android-memcpy-nonnull-me-timbers
[cxx-interop] Builtin functions should ignore return type nullability…
2 parents 5815424 + 6a402a1 commit 466ae71

File tree

4 files changed

+86
-0
lines changed

4 files changed

+86
-0
lines changed

lib/ClangImporter/ImportType.cpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2223,6 +2223,32 @@ ImportedType ClangImporter::Implementation::importFunctionReturnType(
22232223
if (auto elaborated =
22242224
dyn_cast<clang::ElaboratedType>(returnType))
22252225
returnType = elaborated->desugar();
2226+
// In C interop mode, the return type of library builtin functions
2227+
// like 'memcpy' from headers like 'string.h' drops
2228+
// any nullability specifiers from their return type, and preserves it on the
2229+
// declared return type. Thus, in C mode the imported return type of such
2230+
// functions is always optional. However, in C++ interop mode, the return type
2231+
// of builtin functions can preseve the nullability specifiers on their return
2232+
// type, and thus the imported return type of such functions can be
2233+
// non-optional, if the type is annotated with
2234+
// `_Nonnull`. The difference between these two modes can break cross-module
2235+
// Swift serialization, as Swift will no longer be able to resolve an x-ref
2236+
// such as 'memcpy' from a Swift module that uses C interop, within a Swift
2237+
// context that uses C++ interop. In order to avoid the x-ref resolution
2238+
// failure, normalize the return type's nullability for builtin functions in
2239+
// C++ interop mode, to match the imported type in C interop mode.
2240+
auto builtinContext = clang::Builtin::Context();
2241+
if (SwiftContext.LangOpts.EnableCXXInterop && clangDecl->getBuiltinID() &&
2242+
!builtinContext.isTSBuiltin(clangDecl->getBuiltinID()) &&
2243+
builtinContext.isPredefinedLibFunction(
2244+
clangDecl->getBuiltinID()) &&
2245+
builtinContext.getHeaderName(clangDecl->getBuiltinID()) ==
2246+
StringRef("string.h")) {
2247+
if (const auto ART = dyn_cast<clang::AttributedType>(returnType)) {
2248+
if (ART->getImmediateNullability())
2249+
clang::AttributedType::stripOuterNullability(returnType);
2250+
}
2251+
}
22262252

22272253
// Specialized templates need to match the args/result exactly (i.e.,
22282254
// ptr -> ptr not ptr -> Optional<ptr>).
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
#pragma once
2+
3+
#include <stddef.h>
4+
5+
#define __attribute_pure__ __attribute__((__pure__))
6+
7+
#ifdef __cplusplus
8+
extern "C" {
9+
#endif
10+
11+
#ifdef __cplusplus
12+
#define TEST_CONST_RETURN const
13+
#else
14+
#define TEST_CONST_RETURN
15+
#endif
16+
17+
18+
void* _Nonnull memcpy(void* _Nonnull, const void* _Nonnull, size_t);
19+
20+
void* _Nonnull memcpy42(void* _Nonnull, const void* _Nonnull, size_t);
21+
22+
void TEST_CONST_RETURN* _Nullable memchr(const void* _Nonnull __s, int __ch, size_t __n) __attribute_pure__;
23+
24+
void* _Nonnull memmove(void* _Nonnull __dst, const void* _Nonnull __src, size_t __n);
25+
26+
void* _Nonnull memset(void* _Nonnull __dst, int __ch, size_t __n);
27+
28+
char TEST_CONST_RETURN* strrchr(const char* __s, int __ch) __attribute_pure__;
29+
30+
char* _Nonnull strcpy(char* _Nonnull __dst, const char* _Nonnull __src);
31+
char* _Nonnull strcat(char* _Nonnull __dst, const char* _Nonnull __src);
32+
33+
#ifdef __cplusplus
34+
}
35+
#endif

test/Interop/Cxx/function/Inputs/module.modulemap

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,8 @@ module DefaultArguments {
22
header "default-arguments.h"
33
export *
44
}
5+
6+
module CustomStringBuiltins {
7+
header "custom-string-builtins.h"
8+
export *
9+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk-nosource) -typecheck -verify -verify-ignore-unknown -I %S/Inputs -cxx-interoperability-mode=default -Xcc -D_CRT_SECURE_NO_WARNINGS %s
2+
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk-nosource) -typecheck -verify -verify-ignore-unknown -I %S/Inputs -Xcc -D_CRT_SECURE_NO_WARNINGS %s
3+
4+
import CustomStringBuiltins
5+
6+
public func testMemcpyOptionalReturn(p: UnsafeMutableRawPointer, e: UnsafeRawPointer, c: UnsafePointer<CChar>, pc: UnsafeMutablePointer<CChar>) {
7+
// This 'memcpy' is a builtin and is always an optional, regardless of _Nonnull.
8+
let x = CustomStringBuiltins.memcpy(p, e, 1)!
9+
10+
// Not a builtin, _Nonnull makes it a non-optional.
11+
let y = CustomStringBuiltins.memcpy42(p, e, 1)! // expected-error {{cannot force unwrap value of non-optional type 'UnsafeMutableRawPointer'}}
12+
13+
// other builtins from 'string.h'
14+
let _ = CustomStringBuiltins.memchr(e, 42, 1)!
15+
let _ = CustomStringBuiltins.memmove(p, e, 42)!
16+
let _ = CustomStringBuiltins.memset(p, 1, 42)!
17+
let _ = CustomStringBuiltins.strrchr(c, 0)!
18+
let _ = CustomStringBuiltins.strcpy(pc, c)!
19+
let _ = CustomStringBuiltins.strcat(pc, c)!
20+
}

0 commit comments

Comments
 (0)