Skip to content

Commit 3748b0f

Browse files
authored
Merge pull request #65129 from hyp/eng/no-evo-cxx
[interop] Prohibit use of C++ APIs in public interfaces that opt-in i…
2 parents 19f4b6f + 045fcf3 commit 3748b0f

13 files changed

+355
-13
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3370,7 +3370,8 @@ ERROR(decl_from_hidden_module,none,
33703370
"it is an SPI imported from %3|"
33713371
"it is SPI|"
33723372
"%3 was imported for SPI only|"
3373-
"%3 was not imported by this file}4",
3373+
"%3 was not imported by this file|"
3374+
"C++ types from imported module %3 do not support library evolution}4",
33743375
(DescriptiveDeclKind, DeclName, unsigned, Identifier, unsigned))
33753376
WARNING(decl_from_hidden_module_warn,none,
33763377
"cannot use %0 %1 %select{here|as property wrapper here|"
@@ -3391,7 +3392,8 @@ ERROR(typealias_desugars_to_type_from_hidden_module,none,
33913392
"it is an SPI imported from %4|"
33923393
"<<ERROR>>|"
33933394
"%4 was imported for SPI only|"
3394-
"%4 was not imported by this file}5",
3395+
"%4 was not imported by this file|"
3396+
"C++ types from imported module %4 do not support library evolution}5",
33953397
(DeclName, StringRef, StringRef, unsigned, Identifier, unsigned))
33963398
ERROR(conformance_from_implementation_only_module,none,
33973399
"cannot use conformance of %0 to %1 %select{here|as property wrapper here|"
@@ -3402,7 +3404,8 @@ ERROR(conformance_from_implementation_only_module,none,
34023404
"the conformance is declared as SPI in %3|"
34033405
"the conformance is declared as SPI|"
34043406
"%3 was imported for SPI only|"
3405-
"%3 was not imported by this file}4",
3407+
"%3 was not imported by this file|"
3408+
"C++ types from imported module %3 do not support library evolution}4",
34063409
(Type, Identifier, unsigned, Identifier, unsigned))
34073410
NOTE(assoc_conformance_from_implementation_only_module,none,
34083411
"in associated type %0 (inferred as %1)", (Type, Type))
@@ -6513,7 +6516,8 @@ ERROR(inlinable_decl_ref_from_hidden_module,
65136516
"it is an SPI imported from %3|"
65146517
"it is SPI|"
65156518
"%3 was imported for SPI only|"
6516-
"%3 was not imported by this file}4",
6519+
"%3 was not imported by this file|"
6520+
"C++ APIs from imported module %3 do not support library evolution}4",
65176521
(DescriptiveDeclKind, DeclName, unsigned, Identifier, unsigned))
65186522

65196523
WARNING(inlinable_decl_ref_from_hidden_module_warn,
@@ -6529,7 +6533,8 @@ ERROR(inlinable_typealias_desugars_to_type_from_hidden_module,
65296533
"it is an SPI imported from %4|"
65306534
"<<ERROR>>|"
65316535
"%4 was imported for SPI only|"
6532-
"%4 was not imported by this file}5",
6536+
"%4 was not imported by this file|"
6537+
"C++ types from imported module %4 do not support library evolution}5",
65336538
(DeclName, StringRef, StringRef, unsigned, Identifier, unsigned))
65346539

65356540
NOTE(missing_import_inserted,

lib/Sema/TypeCheckAccess.cpp

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include "swift/AST/Pattern.h"
2727
#include "swift/AST/ParameterList.h"
2828
#include "swift/AST/TypeCheckRequests.h"
29+
#include "clang/AST/DeclObjC.h"
2930

3031
using namespace swift;
3132

@@ -1789,6 +1790,66 @@ class UsableFromInlineChecker : public AccessControlCheckerBase,
17891790
}
17901791
}
17911792
};
1793+
1794+
bool isFragileClangType(clang::QualType type) {
1795+
if (type.isNull())
1796+
return true;
1797+
auto underlyingTypePtr = type->getUnqualifiedDesugaredType();
1798+
// Objective-C types are compatible with library
1799+
// evolution.
1800+
if (underlyingTypePtr->isObjCObjectPointerType())
1801+
return false;
1802+
// Builtin clang types are compatible with library evolution.
1803+
if (underlyingTypePtr->isBuiltinType())
1804+
return false;
1805+
// Pointers to non-fragile types are non-fragile.
1806+
if (underlyingTypePtr->isPointerType())
1807+
return isFragileClangType(underlyingTypePtr->getPointeeType());
1808+
return true;
1809+
}
1810+
1811+
bool isFragileClangNode(const ClangNode &node) {
1812+
auto *decl = node.getAsDecl();
1813+
if (!decl)
1814+
return false;
1815+
// Namespaces by themselves don't impact ABI.
1816+
if (isa<clang::NamespaceDecl>(decl))
1817+
return false;
1818+
// Objective-C type declarations are compatible with library evolution.
1819+
if (isa<clang::ObjCContainerDecl>(decl))
1820+
return false;
1821+
if (auto *fd = dyn_cast<clang::FunctionDecl>(decl)) {
1822+
if (!isa<clang::CXXMethodDecl>(decl) &&
1823+
!isFragileClangType(fd->getDeclaredReturnType())) {
1824+
for (const auto *param : fd->parameters()) {
1825+
if (isFragileClangType(param->getType()))
1826+
return true;
1827+
}
1828+
// A global function whose return and parameter types are compatible with
1829+
// library evolution is compatible with library evolution.
1830+
return false;
1831+
}
1832+
}
1833+
if (auto *md = dyn_cast<clang::ObjCMethodDecl>(decl)) {
1834+
if (!isFragileClangType(md->getReturnType())) {
1835+
for (const auto *param : md->parameters()) {
1836+
if (isFragileClangType(param->getType()))
1837+
return true;
1838+
}
1839+
// An Objective-C method whose return and parameter types are compatible
1840+
// with library evolution is compatible with library evolution.
1841+
return false;
1842+
}
1843+
}
1844+
// An Objective-C property whose can be compatible
1845+
// with library evolution if its type is compatible.
1846+
if (auto *pd = dyn_cast<clang::ObjCPropertyDecl>(decl))
1847+
return isFragileClangType(pd->getType());
1848+
if (auto *typedefDecl = dyn_cast<clang::TypedefNameDecl>(decl))
1849+
return isFragileClangType(typedefDecl->getUnderlyingType());
1850+
return true;
1851+
}
1852+
17921853
} // end anonymous namespace
17931854

17941855
/// Returns the kind of origin, implementation-only import or SPI declaration,
@@ -1895,6 +1956,14 @@ swift::getDisallowedOriginKind(const Decl *decl,
18951956
DisallowedOriginKind::SPIImported;
18961957
}
18971958

1959+
// C++ APIs do not support library evolution.
1960+
if (SF->getASTContext().LangOpts.EnableCXXInterop && where.getDeclContext() &&
1961+
where.getDeclContext()->getAsDecl() &&
1962+
where.getDeclContext()->getAsDecl()->getModuleContext()->isResilient() &&
1963+
decl->hasClangNode() && !decl->getModuleContext()->isSwiftShimsModule() &&
1964+
isFragileClangNode(decl->getClangNode()))
1965+
return DisallowedOriginKind::FragileCxxAPI;
1966+
18981967
return DisallowedOriginKind::None;
18991968
}
19001969

lib/Sema/TypeCheckAccess.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ enum class DisallowedOriginKind : uint8_t {
4646
SPILocal,
4747
SPIOnly,
4848
MissingImport,
49+
FragileCxxAPI,
4950
None
5051
};
5152

stdlib/cmake/modules/AddSwiftStdlib.cmake

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -657,6 +657,7 @@ endfunction()
657657
# [IS_STDLIB]
658658
# [IS_STDLIB_CORE]
659659
# [IS_SDK_OVERLAY]
660+
# [IS_FRAGILE]
660661
# INSTALL_IN_COMPONENT comp
661662
# MACCATALYST_BUILD_FLAVOR flavor
662663
# source1 [source2 source3 ...])
@@ -723,6 +724,11 @@ endfunction()
723724
#
724725
# MACCATALYST_BUILD_FLAVOR
725726
# Possible values are 'ios-like', 'macos-like', 'zippered', 'unzippered-twin'
727+
728+
# IS_FRAGILE
729+
# Disable library evolution even
730+
# if this library is part of the SDK.
731+
726732
#
727733
# source1 ...
728734
# Sources to add into this library
@@ -737,7 +743,8 @@ function(add_swift_target_library_single target name)
737743
SHARED
738744
STATIC
739745
NO_LINK_NAME
740-
INSTALL_WITH_SHARED)
746+
INSTALL_WITH_SHARED
747+
IS_FRAGILE)
741748
set(SWIFTLIB_SINGLE_single_parameter_options
742749
ARCHITECTURE
743750
DEPLOYMENT_VERSION_IOS
@@ -946,6 +953,7 @@ function(add_swift_target_library_single target name)
946953
${SWIFTLIB_SINGLE_IS_STDLIB_keyword}
947954
${SWIFTLIB_SINGLE_IS_STDLIB_CORE_keyword}
948955
${SWIFTLIB_SINGLE_IS_SDK_OVERLAY_keyword}
956+
${SWIFTLIB_SINGLE_IS_FRAGILE_keyword}
949957
${embed_bitcode_arg}
950958
${SWIFTLIB_SINGLE_STATIC_keyword}
951959
${SWIFTLIB_SINGLE_NO_LINK_NAME_keyword}

stdlib/cmake/modules/SwiftSource.cmake

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ function(handle_swift_sources
4949
dependency_sibgen_target_out_var_name
5050
sourcesvar externalvar name)
5151
cmake_parse_arguments(SWIFTSOURCES
52-
"IS_MAIN;IS_STDLIB;IS_STDLIB_CORE;IS_SDK_OVERLAY;EMBED_BITCODE;STATIC;NO_LINK_NAME"
52+
"IS_MAIN;IS_STDLIB;IS_STDLIB_CORE;IS_SDK_OVERLAY;EMBED_BITCODE;STATIC;NO_LINK_NAME;IS_FRAGILE"
5353
"SDK;ARCHITECTURE;INSTALL_IN_COMPONENT;MACCATALYST_BUILD_FLAVOR;BOOTSTRAPPING"
5454
"DEPENDS;COMPILE_FLAGS;MODULE_NAME;ENABLE_LTO"
5555
${ARGN})
@@ -64,6 +64,7 @@ function(handle_swift_sources
6464
translate_flag(${SWIFTSOURCES_STATIC} "STATIC"
6565
STATIC_arg)
6666
translate_flag(${SWIFTSOURCES_NO_LINK_NAME} "NO_LINK_NAME" NO_LINK_NAME_arg)
67+
translate_flag(${SWIFTSOURCES_IS_FRAGILE} "IS_FRAGILE" IS_FRAGILE_arg)
6768
if(DEFINED SWIFTSOURCES_BOOTSTRAPPING)
6869
set(BOOTSTRAPPING_arg "BOOTSTRAPPING" ${SWIFTSOURCES_BOOTSTRAPPING})
6970
endif()
@@ -152,6 +153,7 @@ function(handle_swift_sources
152153
${EMBED_BITCODE_arg}
153154
${STATIC_arg}
154155
${BOOTSTRAPPING_arg}
156+
${IS_FRAGILE_arg}
155157
INSTALL_IN_COMPONENT "${SWIFTSOURCES_INSTALL_IN_COMPONENT}"
156158
MACCATALYST_BUILD_FLAVOR "${SWIFTSOURCES_MACCATALYST_BUILD_FLAVOR}")
157159
set("${dependency_target_out_var_name}" "${dependency_target}" PARENT_SCOPE)
@@ -376,7 +378,7 @@ function(_compile_swift_files
376378
dependency_sib_target_out_var_name dependency_sibopt_target_out_var_name
377379
dependency_sibgen_target_out_var_name)
378380
cmake_parse_arguments(SWIFTFILE
379-
"IS_MAIN;IS_STDLIB;IS_STDLIB_CORE;IS_SDK_OVERLAY;EMBED_BITCODE;STATIC"
381+
"IS_MAIN;IS_STDLIB;IS_STDLIB_CORE;IS_SDK_OVERLAY;EMBED_BITCODE;STATIC;IS_FRAGILE"
380382
"OUTPUT;MODULE_NAME;INSTALL_IN_COMPONENT;MACCATALYST_BUILD_FLAVOR;BOOTSTRAPPING"
381383
"SOURCES;FLAGS;DEPENDS;SDK;ARCHITECTURE;OPT_FLAGS;MODULE_DIR"
382384
${ARGN})
@@ -487,7 +489,7 @@ function(_compile_swift_files
487489
endif()
488490

489491
# The standard library and overlays are built resiliently when SWIFT_STDLIB_STABLE_ABI=On.
490-
if(SWIFTFILE_IS_STDLIB AND SWIFT_STDLIB_STABLE_ABI)
492+
if(SWIFTFILE_IS_STDLIB AND NOT SWIFTFILE_IS_FRAGILE AND SWIFT_STDLIB_STABLE_ABI)
491493
list(APPEND swift_flags "-enable-library-evolution")
492494
list(APPEND swift_flags "-library-level" "api")
493495
list(APPEND swift_flags "-Xfrontend" "-require-explicit-availability=ignore")

stdlib/public/Cxx/std/CMakeLists.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@ endif()
1010
#
1111
# C++ Standard Library Overlay.
1212
#
13-
add_swift_target_library(swiftCxxStdlib STATIC NO_LINK_NAME IS_STDLIB IS_SWIFT_ONLY
13+
# The overlay is fragile (i.e. it does not use library evolution)
14+
# as it's not ABI stable.
15+
add_swift_target_library(swiftCxxStdlib STATIC NO_LINK_NAME IS_STDLIB IS_SWIFT_ONLY IS_FRAGILE
1416
std.swift
1517
String.swift
1618

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: split-file %s %t
3+
// RUN: %target-swift-frontend %t/test.swift -I %t/Inputs -typecheck -enable-library-evolution -enable-experimental-cxx-interop -verify
4+
5+
// REQUIRES: objc_interop
6+
7+
//--- Inputs/module.modulemap
8+
module ObjCxxModule {
9+
header "header.h"
10+
requires cplusplus
11+
}
12+
13+
//--- Inputs/header.h
14+
15+
#import <Foundation/Foundation.h>
16+
17+
class CxxStruct {
18+
public:
19+
int x; int y;
20+
21+
void method() const;
22+
};
23+
24+
@interface ObjCClass: NSObject
25+
26+
- (void)myTestMethod;
27+
- (int *)returnsIntPtr;
28+
29+
- (CxxStruct)testMethodReturnsCxxStruct;
30+
- (void)testMethodTakesCxxStructPtr: (CxxStruct * _Nullable) ptr;
31+
+ (ObjCClass * _Nonnull)getInstance;
32+
33+
@property int intProp;
34+
@property(copy) ObjCClass * _Nonnull objcClassProp;
35+
@property CxxStruct * structPtrProp;
36+
37+
@end
38+
39+
using ObjCClassTypealias = ObjCClass * _Nonnull;
40+
41+
@protocol ObjCProto
42+
43+
- (void)testProto;
44+
45+
@end
46+
47+
using QualIDTypeAlias = id<ObjCProto>;
48+
49+
using BuiltinIntTypealis = int;
50+
51+
//--- test.swift
52+
53+
import ObjCxxModule
54+
55+
// ok
56+
public func usesObjCClass() -> ObjCClass {
57+
return ObjCClass.getInstance()
58+
}
59+
60+
public func usesObjCClassTypealias(_ x: ObjCClassTypealias) {
61+
}
62+
63+
public func usesObjCProto(_ x: ObjCProto) {
64+
}
65+
66+
public func usesQualIDTypeAlias(_ x: QualIDTypeAlias) {
67+
}
68+
69+
public func usesBuiltinIntTypealis() -> BuiltinIntTypealis {
70+
return 21
71+
}
72+
73+
@inlinable
74+
public func publicFuncPublicBody() {
75+
let i = ObjCClass.getInstance()
76+
i.myTestMethod()
77+
i.returnsIntPtr()
78+
let _ = i.intProp
79+
let _ = i.objcClassProp
80+
// expected-error@+1 {{instance method 'testMethodReturnsCxxStruct()' cannot be used in an '@inlinable' function because C++ APIs from imported module 'ObjCxxModule' do not support library evolution}}
81+
i.testMethodReturnsCxxStruct()
82+
// expected-error@+1 {{instance method 'testMethodTakesCxxStructPtr' cannot be used in an '@inlinable' function because C++ APIs from imported module 'ObjCxxModule' do not support library evolution}}
83+
i.testMethodTakesCxxStructPtr(nil)
84+
// expected-error@+1 {{property 'structPtrProp' cannot be used in an '@inlinable' function because C++ APIs from imported module 'ObjCxxModule' do not support library evolution}}
85+
let _ = i.structPtrProp
86+
}
87+
88+
// expected-error@+1 {{cannot use struct 'CxxStruct' here; C++ types from imported module 'ObjCxxModule' do not support library evolution}}
89+
public func usesCxxStruct(_ x: CxxStruct) {
90+
}

0 commit comments

Comments
 (0)