Skip to content

Commit 62a2f0f

Browse files
authored
[APINotes] Add support for SWIFT_RETURED_AS_UNRETAINED_BY_DEFAULT (#138699)
This patch adds support in APINotes for annotating C++ user-defined types with: `swift_attr("returned_as_unretained_by_default")` This attribute allows to specify a default ownership convention for return values of `SWIFT_SHARED_REFERENCE` c++ types. Specifically, it marks all unannotated return values of this type as `unretained` (`+0`) by default, unless explicitly overridden at the API level using `swift_attr("returns_retained")` or `swift_attr("returns_unretained")`. The corresponding Swift compiler support for this annotation enables developers to suppress warnings about unannotated return ownership in large codebases while maintaining safe and predictable ownership semantics. By enabling this in APINotes, library authors can define this behavior externally without needing to modify C++ source headers directly. ### Example usage in APINotes: ``` - Name: RefCountedTypeWithDefaultConvention SwiftImportAs: reference SwiftDefaultOwnership: unretained ``` rdar://150764491
1 parent 43c514b commit 62a2f0f

File tree

9 files changed

+43
-1
lines changed

9 files changed

+43
-1
lines changed

clang/include/clang/APINotes/Types.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -737,6 +737,7 @@ class TagInfo : public CommonTypeInfo {
737737
std::optional<std::string> SwiftImportAs;
738738
std::optional<std::string> SwiftRetainOp;
739739
std::optional<std::string> SwiftReleaseOp;
740+
std::optional<std::string> SwiftDefaultOwnership;
740741

741742
/// The Swift protocol that this type should be automatically conformed to.
742743
std::optional<std::string> SwiftConformance;
@@ -786,6 +787,8 @@ class TagInfo : public CommonTypeInfo {
786787
SwiftRetainOp = RHS.SwiftRetainOp;
787788
if (!SwiftReleaseOp)
788789
SwiftReleaseOp = RHS.SwiftReleaseOp;
790+
if (!SwiftDefaultOwnership)
791+
SwiftDefaultOwnership = RHS.SwiftDefaultOwnership;
789792

790793
if (!SwiftConformance)
791794
SwiftConformance = RHS.SwiftConformance;
@@ -815,6 +818,7 @@ inline bool operator==(const TagInfo &LHS, const TagInfo &RHS) {
815818
LHS.SwiftImportAs == RHS.SwiftImportAs &&
816819
LHS.SwiftRetainOp == RHS.SwiftRetainOp &&
817820
LHS.SwiftReleaseOp == RHS.SwiftReleaseOp &&
821+
LHS.SwiftDefaultOwnership == RHS.SwiftDefaultOwnership &&
818822
LHS.SwiftConformance == RHS.SwiftConformance &&
819823
LHS.isFlagEnum() == RHS.isFlagEnum() &&
820824
LHS.isSwiftCopyable() == RHS.isSwiftCopyable() &&

clang/lib/APINotes/APINotesFormat.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ const uint16_t VERSION_MAJOR = 0;
2424
/// API notes file minor version number.
2525
///
2626
/// When the format changes IN ANY WAY, this number should be incremented.
27-
const uint16_t VERSION_MINOR = 34; // SwiftReturnOwnership
27+
const uint16_t VERSION_MINOR = 35; // SwiftDefaultOwnership
2828

2929
const uint8_t kSwiftConforms = 1;
3030
const uint8_t kSwiftDoesNotConform = 2;

clang/lib/APINotes/APINotesReader.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -624,6 +624,13 @@ class TagTableInfo
624624
ReleaseOpLength - 1);
625625
Data += ReleaseOpLength - 1;
626626
}
627+
unsigned DefaultOwnershipLength =
628+
endian::readNext<uint16_t, llvm::endianness::little>(Data);
629+
if (DefaultOwnershipLength > 0) {
630+
Info.SwiftDefaultOwnership = std::string(
631+
reinterpret_cast<const char *>(Data), DefaultOwnershipLength - 1);
632+
Data += DefaultOwnershipLength - 1;
633+
}
627634
if (unsigned ConformanceLength =
628635
endian::readNext<uint16_t, llvm::endianness::little>(Data)) {
629636
Info.SwiftConformance = std::string(reinterpret_cast<const char *>(Data),

clang/lib/APINotes/APINotesWriter.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1274,6 +1274,7 @@ class TagTableInfo : public CommonTypeTableInfo<TagTableInfo, TagInfo> {
12741274
return 2 + (TI.SwiftImportAs ? TI.SwiftImportAs->size() : 0) +
12751275
2 + (TI.SwiftRetainOp ? TI.SwiftRetainOp->size() : 0) +
12761276
2 + (TI.SwiftReleaseOp ? TI.SwiftReleaseOp->size() : 0) +
1277+
2 + (TI.SwiftDefaultOwnership ? TI.SwiftDefaultOwnership->size() : 0) +
12771278
2 + (TI.SwiftConformance ? TI.SwiftConformance->size() : 0) +
12781279
3 + getCommonTypeInfoSize(TI);
12791280
// clang-format on
@@ -1322,6 +1323,12 @@ class TagTableInfo : public CommonTypeTableInfo<TagTableInfo, TagInfo> {
13221323
} else {
13231324
writer.write<uint16_t>(0);
13241325
}
1326+
if (auto DefaultOwnership = TI.SwiftDefaultOwnership) {
1327+
writer.write<uint16_t>(DefaultOwnership->size() + 1);
1328+
OS.write(DefaultOwnership->c_str(), DefaultOwnership->size());
1329+
} else {
1330+
writer.write<uint16_t>(0);
1331+
}
13251332
if (auto Conformance = TI.SwiftConformance) {
13261333
writer.write<uint16_t>(Conformance->size() + 1);
13271334
OS.write(Conformance->c_str(), Conformance->size());

clang/lib/APINotes/APINotesYAMLCompiler.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -460,6 +460,7 @@ struct Tag {
460460
std::optional<std::string> SwiftImportAs;
461461
std::optional<std::string> SwiftRetainOp;
462462
std::optional<std::string> SwiftReleaseOp;
463+
std::optional<std::string> SwiftDefaultOwnership;
463464
std::optional<std::string> SwiftConformance;
464465
std::optional<EnumExtensibilityKind> EnumExtensibility;
465466
std::optional<bool> FlagEnum;
@@ -500,6 +501,7 @@ template <> struct MappingTraits<Tag> {
500501
IO.mapOptional("SwiftImportAs", T.SwiftImportAs);
501502
IO.mapOptional("SwiftReleaseOp", T.SwiftReleaseOp);
502503
IO.mapOptional("SwiftRetainOp", T.SwiftRetainOp);
504+
IO.mapOptional("SwiftDefaultOwnership", T.SwiftDefaultOwnership);
503505
IO.mapOptional("SwiftConformsTo", T.SwiftConformance);
504506
IO.mapOptional("EnumExtensibility", T.EnumExtensibility);
505507
IO.mapOptional("FlagEnum", T.FlagEnum);
@@ -990,6 +992,8 @@ class YAMLConverter {
990992
TI.SwiftReleaseOp = T.SwiftReleaseOp;
991993
if (T.SwiftConformance)
992994
TI.SwiftConformance = T.SwiftConformance;
995+
if (T.SwiftDefaultOwnership)
996+
TI.SwiftDefaultOwnership = T.SwiftDefaultOwnership;
993997

994998
if (T.SwiftCopyable)
995999
TI.setSwiftCopyable(T.SwiftCopyable);

clang/lib/Sema/SemaAPINotes.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -643,6 +643,9 @@ static void ProcessAPINotes(Sema &S, TagDecl *D, const api_notes::TagInfo &Info,
643643
if (auto ReleaseOp = Info.SwiftReleaseOp)
644644
D->addAttr(
645645
SwiftAttrAttr::Create(S.Context, "release:" + ReleaseOp.value()));
646+
if (auto DefaultOwnership = Info.SwiftDefaultOwnership)
647+
D->addAttr(SwiftAttrAttr::Create(
648+
S.Context, "returned_as_" + DefaultOwnership.value() + "_by_default"));
646649

647650
if (auto ConformsTo = Info.SwiftConformance)
648651
D->addAttr(

clang/test/APINotes/Inputs/Headers/SwiftImportAs.apinotes

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,11 @@ Tags:
1414
SwiftReleaseOp: RCRelease
1515
SwiftRetainOp: RCRetain
1616
SwiftConformsTo: MySwiftModule.MySwiftRefCountedProtocol
17+
- Name: RefCountedTypeWithDefaultConvention
18+
SwiftImportAs: reference
19+
SwiftReleaseOp: release
20+
SwiftRetainOp: retain
21+
SwiftDefaultOwnership: unretained
1722
- Name: NonCopyableType
1823
SwiftCopyable: false
1924
SwiftConformsTo: MySwiftModule.MySwiftNonCopyableProtocol

clang/test/APINotes/Inputs/Headers/SwiftImportAs.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,7 @@ struct CopyableType { int value; };
1919

2020
struct NonEscapableType { int value; };
2121
struct EscapableType { int value; };
22+
23+
struct RefCountedTypeWithDefaultConvention {};
24+
inline void retain(RefCountedType *x) {}
25+
inline void release(RefCountedType *x) {}

clang/test/APINotes/swift-import-as.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// RUN: %clang_cc1 -fmodules -fblocks -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fdisable-module-hash -fapinotes-modules -fsyntax-only -I %S/Inputs/Headers %s -x c++
33
// RUN: %clang_cc1 -fmodules -fblocks -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fdisable-module-hash -fapinotes-modules -I %S/Inputs/Headers %s -x c++ -ast-dump -ast-dump-filter ImmortalRefType | FileCheck -check-prefix=CHECK-IMMORTAL %s
44
// RUN: %clang_cc1 -fmodules -fblocks -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fdisable-module-hash -fapinotes-modules -I %S/Inputs/Headers %s -x c++ -ast-dump -ast-dump-filter RefCountedType | FileCheck -check-prefix=CHECK-REF-COUNTED %s
5+
// RUN: %clang_cc1 -fmodules -fblocks -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fdisable-module-hash -fapinotes-modules -I %S/Inputs/Headers %s -x c++ -ast-dump -ast-dump-filter RefCountedTypeWithDefaultConvention | FileCheck -check-prefix=CHECK-REF-COUNTED-DEFAULT %s
56
// RUN: %clang_cc1 -fmodules -fblocks -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fdisable-module-hash -fapinotes-modules -I %S/Inputs/Headers %s -x c++ -ast-dump -ast-dump-filter NonCopyableType | FileCheck -check-prefix=CHECK-NON-COPYABLE %s
67
// RUN: %clang_cc1 -fmodules -fblocks -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fdisable-module-hash -fapinotes-modules -I %S/Inputs/Headers %s -x c++ -ast-dump -ast-dump-filter CopyableType | FileCheck -check-prefix=CHECK-COPYABLE %s
78
// RUN: %clang_cc1 -fmodules -fblocks -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fdisable-module-hash -fapinotes-modules -I %S/Inputs/Headers %s -x c++ -ast-dump -ast-dump-filter NonEscapableType | FileCheck -check-prefix=CHECK-NON-ESCAPABLE %s
@@ -26,6 +27,13 @@
2627
// CHECK-REF-COUNTED: SwiftAttrAttr {{.+}} <<invalid sloc>> "release:RCRelease"
2728
// CHECK-REF-COUNTED: SwiftAttrAttr {{.+}} <<invalid sloc>> "conforms_to:MySwiftModule.MySwiftRefCountedProtocol"
2829

30+
// CHECK-REF-COUNTED-DEFAULT: Dumping RefCountedTypeWithDefaultConvention:
31+
// CHECK-REF-COUNTED-DEFAULT-NEXT: CXXRecordDecl {{.+}} imported in SwiftImportAs {{.+}} struct RefCountedTypeWithDefaultConvention
32+
// CHECK-REF-COUNTED-DEFAULT: SwiftAttrAttr {{.+}} <<invalid sloc>> "import_reference"
33+
// CHECK-REF-COUNTED-DEFAULT: SwiftAttrAttr {{.+}} <<invalid sloc>> "retain:retain"
34+
// CHECK-REF-COUNTED-DEFAULT: SwiftAttrAttr {{.+}} <<invalid sloc>> "release:release"
35+
// CHECK-REF-COUNTED-DEFAULT: SwiftAttrAttr {{.+}} <<invalid sloc>> "returned_as_unretained_by_default"
36+
2937
// CHECK-NON-COPYABLE: Dumping NonCopyableType:
3038
// CHECK-NON-COPYABLE-NEXT: CXXRecordDecl {{.+}} imported in SwiftImportAs {{.+}} struct NonCopyableType
3139
// CHECK-NON-COPYABLE: SwiftAttrAttr {{.+}} <<invalid sloc>> "conforms_to:MySwiftModule.MySwiftNonCopyableProtocol"

0 commit comments

Comments
 (0)