Skip to content

Commit 045fcf3

Browse files
committed
[interop] Prohibit use of C++ APIs in public interfaces that opt-in into library evolution
The CxxStdlib overlay now has to be built without library evolution enabled.
1 parent 66641c7 commit 045fcf3

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
@@ -3187,7 +3187,8 @@ ERROR(decl_from_hidden_module,none,
31873187
"it is an SPI imported from %3|"
31883188
"it is SPI|"
31893189
"%3 was imported for SPI only|"
3190-
"%3 was not imported by this file}4",
3190+
"%3 was not imported by this file|"
3191+
"C++ types from imported module %3 do not support library evolution}4",
31913192
(DescriptiveDeclKind, DeclName, unsigned, Identifier, unsigned))
31923193
WARNING(decl_from_hidden_module_warn,none,
31933194
"cannot use %0 %1 %select{here|as property wrapper here|"
@@ -3208,7 +3209,8 @@ ERROR(typealias_desugars_to_type_from_hidden_module,none,
32083209
"it is an SPI imported from %4|"
32093210
"<<ERROR>>|"
32103211
"%4 was imported for SPI only|"
3211-
"%4 was not imported by this file}5",
3212+
"%4 was not imported by this file|"
3213+
"C++ types from imported module %4 do not support library evolution}5",
32123214
(DeclName, StringRef, StringRef, unsigned, Identifier, unsigned))
32133215
ERROR(conformance_from_implementation_only_module,none,
32143216
"cannot use conformance of %0 to %1 %select{here|as property wrapper here|"
@@ -3219,7 +3221,8 @@ ERROR(conformance_from_implementation_only_module,none,
32193221
"the conformance is declared as SPI in %3|"
32203222
"the conformance is declared as SPI|"
32213223
"%3 was imported for SPI only|"
3222-
"%3 was not imported by this file}4",
3224+
"%3 was not imported by this file|"
3225+
"C++ types from imported module %3 do not support library evolution}4",
32233226
(Type, Identifier, unsigned, Identifier, unsigned))
32243227
NOTE(assoc_conformance_from_implementation_only_module,none,
32253228
"in associated type %0 (inferred as %1)", (Type, Type))
@@ -6290,7 +6293,8 @@ ERROR(inlinable_decl_ref_from_hidden_module,
62906293
"it is an SPI imported from %3|"
62916294
"it is SPI|"
62926295
"%3 was imported for SPI only|"
6293-
"%3 was not imported by this file}4",
6296+
"%3 was not imported by this file|"
6297+
"C++ APIs from imported module %3 do not support library evolution}4",
62946298
(DescriptiveDeclKind, DeclName, unsigned, Identifier, unsigned))
62956299

62966300
WARNING(inlinable_decl_ref_from_hidden_module_warn,
@@ -6306,7 +6310,8 @@ ERROR(inlinable_typealias_desugars_to_type_from_hidden_module,
63066310
"it is an SPI imported from %4|"
63076311
"<<ERROR>>|"
63086312
"%4 was imported for SPI only|"
6309-
"%4 was not imported by this file}5",
6313+
"%4 was not imported by this file|"
6314+
"C++ types from imported module %4 do not support library evolution}5",
63106315
(DeclName, StringRef, StringRef, unsigned, Identifier, unsigned))
63116316

63126317
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

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

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

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

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
@@ -643,6 +643,7 @@ endfunction()
643643
# [IS_STDLIB]
644644
# [IS_STDLIB_CORE]
645645
# [IS_SDK_OVERLAY]
646+
# [IS_FRAGILE]
646647
# INSTALL_IN_COMPONENT comp
647648
# MACCATALYST_BUILD_FLAVOR flavor
648649
# source1 [source2 source3 ...])
@@ -709,6 +710,11 @@ endfunction()
709710
#
710711
# MACCATALYST_BUILD_FLAVOR
711712
# Possible values are 'ios-like', 'macos-like', 'zippered', 'unzippered-twin'
713+
714+
# IS_FRAGILE
715+
# Disable library evolution even
716+
# if this library is part of the SDK.
717+
712718
#
713719
# source1 ...
714720
# Sources to add into this library
@@ -723,7 +729,8 @@ function(add_swift_target_library_single target name)
723729
SHARED
724730
STATIC
725731
NO_LINK_NAME
726-
INSTALL_WITH_SHARED)
732+
INSTALL_WITH_SHARED
733+
IS_FRAGILE)
727734
set(SWIFTLIB_SINGLE_single_parameter_options
728735
ARCHITECTURE
729736
DEPLOYMENT_VERSION_IOS
@@ -932,6 +939,7 @@ function(add_swift_target_library_single target name)
932939
${SWIFTLIB_SINGLE_IS_STDLIB_keyword}
933940
${SWIFTLIB_SINGLE_IS_STDLIB_CORE_keyword}
934941
${SWIFTLIB_SINGLE_IS_SDK_OVERLAY_keyword}
942+
${SWIFTLIB_SINGLE_IS_FRAGILE_keyword}
935943
${embed_bitcode_arg}
936944
${SWIFTLIB_SINGLE_STATIC_keyword}
937945
${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
@@ -127,7 +127,9 @@ add_dependencies(sdk-overlay libstdcxx-modulemap)
127127
#
128128
# C++ Standard Library Overlay.
129129
#
130-
add_swift_target_library(swiftCxxStdlib STATIC NO_LINK_NAME IS_STDLIB IS_SWIFT_ONLY
130+
# The overlay is fragile (i.e. it does not use library evolution)
131+
# as it's not ABI stable.
132+
add_swift_target_library(swiftCxxStdlib STATIC NO_LINK_NAME IS_STDLIB IS_SWIFT_ONLY IS_FRAGILE
131133
std.swift
132134
String.swift
133135

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)