[API notes] Allow SwiftConformsTo on Typedefs

SwiftConformsTo specifies an additional conformance that should be
applied on import. Allow this on typedefs, because those can be imported
as wrapper types.
This commit is contained in:
Doug Gregor 2025-08-12 11:57:43 -07:00 committed by GitHub
parent a02444fb69
commit 2b37471f72
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 58 additions and 31 deletions

View File

@ -141,6 +141,9 @@ class CommonTypeInfo : public CommonEntityInfo {
/// The NS error domain for this type.
std::optional<std::string> NSErrorDomain;
/// The Swift protocol that this type should be automatically conformed to.
std::optional<std::string> SwiftConformance;
public:
CommonTypeInfo() {}
@ -165,6 +168,14 @@ public:
: std::nullopt;
}
std::optional<std::string> getSwiftConformance() const {
return SwiftConformance;
}
void setSwiftConformance(std::optional<std::string> conformance) {
SwiftConformance = conformance;
}
friend bool operator==(const CommonTypeInfo &, const CommonTypeInfo &);
CommonTypeInfo &operator|=(const CommonTypeInfo &RHS) {
@ -175,6 +186,8 @@ public:
setSwiftBridge(RHS.getSwiftBridge());
if (!NSErrorDomain)
setNSErrorDomain(RHS.getNSErrorDomain());
if (SwiftConformance)
setSwiftConformance(RHS.getSwiftConformance());
return *this;
}
@ -185,7 +198,8 @@ public:
inline bool operator==(const CommonTypeInfo &LHS, const CommonTypeInfo &RHS) {
return static_cast<const CommonEntityInfo &>(LHS) == RHS &&
LHS.SwiftBridge == RHS.SwiftBridge &&
LHS.NSErrorDomain == RHS.NSErrorDomain;
LHS.NSErrorDomain == RHS.NSErrorDomain &&
LHS.SwiftConformance == RHS.SwiftConformance;
}
inline bool operator!=(const CommonTypeInfo &LHS, const CommonTypeInfo &RHS) {
@ -739,9 +753,6 @@ public:
std::optional<std::string> SwiftReleaseOp;
std::optional<std::string> SwiftDefaultOwnership;
/// The Swift protocol that this type should be automatically conformed to.
std::optional<std::string> SwiftConformance;
std::optional<EnumExtensibilityKind> EnumExtensibility;
TagInfo()
@ -790,9 +801,6 @@ public:
if (!SwiftDefaultOwnership)
SwiftDefaultOwnership = RHS.SwiftDefaultOwnership;
if (!SwiftConformance)
SwiftConformance = RHS.SwiftConformance;
if (!HasFlagEnum)
setFlagEnum(RHS.isFlagEnum());
@ -819,7 +827,6 @@ inline bool operator==(const TagInfo &LHS, const TagInfo &RHS) {
LHS.SwiftRetainOp == RHS.SwiftRetainOp &&
LHS.SwiftReleaseOp == RHS.SwiftReleaseOp &&
LHS.SwiftDefaultOwnership == RHS.SwiftDefaultOwnership &&
LHS.SwiftConformance == RHS.SwiftConformance &&
LHS.isFlagEnum() == RHS.isFlagEnum() &&
LHS.isSwiftCopyable() == RHS.isSwiftCopyable() &&
LHS.isSwiftEscapable() == RHS.isSwiftEscapable() &&

View File

@ -24,7 +24,7 @@ const uint16_t VERSION_MAJOR = 0;
/// API notes file minor version number.
///
/// When the format changes IN ANY WAY, this number should be incremented.
const uint16_t VERSION_MINOR = 35; // SwiftDefaultOwnership
const uint16_t VERSION_MINOR = 36; // Typedef SwiftConformsTo
const uint8_t kSwiftConforms = 1;
const uint8_t kSwiftDoesNotConform = 2;

View File

@ -134,6 +134,13 @@ void ReadCommonTypeInfo(const uint8_t *&Data, CommonTypeInfo &Info) {
reinterpret_cast<const char *>(Data), ErrorDomainLength - 1)));
Data += ErrorDomainLength - 1;
}
if (unsigned ConformanceLength =
endian::readNext<uint16_t, llvm::endianness::little>(Data)) {
Info.setSwiftConformance(std::string(reinterpret_cast<const char *>(Data),
ConformanceLength - 1));
Data += ConformanceLength - 1;
}
}
/// Used to deserialize the on-disk identifier table.
@ -629,12 +636,6 @@ public:
reinterpret_cast<const char *>(Data), DefaultOwnershipLength - 1);
Data += DefaultOwnershipLength - 1;
}
if (unsigned ConformanceLength =
endian::readNext<uint16_t, llvm::endianness::little>(Data)) {
Info.SwiftConformance = std::string(reinterpret_cast<const char *>(Data),
ConformanceLength - 1);
Data += ConformanceLength - 1;
}
ReadCommonTypeInfo(Data, Info);
return Info;

View File

@ -536,7 +536,8 @@ unsigned getCommonEntityInfoSize(const CommonEntityInfo &CEI) {
// in on-disk hash tables.
unsigned getCommonTypeInfoSize(const CommonTypeInfo &CTI) {
return 2 + (CTI.getSwiftBridge() ? CTI.getSwiftBridge()->size() : 0) + 2 +
(CTI.getNSErrorDomain() ? CTI.getNSErrorDomain()->size() : 0) +
(CTI.getNSErrorDomain() ? CTI.getNSErrorDomain()->size() : 0) + 2 +
(CTI.getSwiftConformance() ? CTI.getSwiftConformance()->size() : 0) +
getCommonEntityInfoSize(CTI);
}
@ -557,6 +558,12 @@ void emitCommonTypeInfo(raw_ostream &OS, const CommonTypeInfo &CTI) {
} else {
writer.write<uint16_t>(0);
}
if (auto conformance = CTI.getSwiftConformance()) {
writer.write<uint16_t>(conformance->size() + 1);
OS.write(conformance->c_str(), conformance->size());
} else {
writer.write<uint16_t>(0);
}
}
/// Used to serialize the on-disk Objective-C property table.
@ -1274,7 +1281,6 @@ public:
2 + (TI.SwiftRetainOp ? TI.SwiftRetainOp->size() : 0) +
2 + (TI.SwiftReleaseOp ? TI.SwiftReleaseOp->size() : 0) +
2 + (TI.SwiftDefaultOwnership ? TI.SwiftDefaultOwnership->size() : 0) +
2 + (TI.SwiftConformance ? TI.SwiftConformance->size() : 0) +
3 + getCommonTypeInfoSize(TI);
// clang-format on
}
@ -1328,12 +1334,6 @@ public:
} else {
writer.write<uint16_t>(0);
}
if (auto Conformance = TI.SwiftConformance) {
writer.write<uint16_t>(Conformance->size() + 1);
OS.write(Conformance->c_str(), Conformance->size());
} else {
writer.write<uint16_t>(0);
}
emitCommonTypeInfo(OS, TI);
}

View File

@ -251,6 +251,7 @@ struct Class {
std::optional<StringRef> NSErrorDomain;
std::optional<bool> SwiftImportAsNonGeneric;
std::optional<bool> SwiftObjCMembers;
std::optional<std::string> SwiftConformance;
MethodsSeq Methods;
PropertiesSeq Properties;
};
@ -275,6 +276,7 @@ template <> struct MappingTraits<Class> {
IO.mapOptional("NSErrorDomain", C.NSErrorDomain);
IO.mapOptional("SwiftImportAsNonGeneric", C.SwiftImportAsNonGeneric);
IO.mapOptional("SwiftObjCMembers", C.SwiftObjCMembers);
IO.mapOptional("SwiftConformsTo", C.SwiftConformance);
IO.mapOptional("Methods", C.Methods);
IO.mapOptional("Properties", C.Properties);
}
@ -525,6 +527,7 @@ struct Typedef {
std::optional<StringRef> SwiftBridge;
std::optional<StringRef> NSErrorDomain;
std::optional<SwiftNewTypeKind> SwiftType;
std::optional<std::string> SwiftConformance;
};
typedef std::vector<Typedef> TypedefsSeq;
@ -553,6 +556,7 @@ template <> struct MappingTraits<Typedef> {
IO.mapOptional("SwiftBridge", T.SwiftBridge);
IO.mapOptional("NSErrorDomain", T.NSErrorDomain);
IO.mapOptional("SwiftWrapper", T.SwiftType);
IO.mapOptional("SwiftConformsTo", T.SwiftConformance);
}
};
} // namespace yaml
@ -802,6 +806,8 @@ public:
if (Common.SwiftBridge)
Info.setSwiftBridge(std::string(*Common.SwiftBridge));
Info.setNSErrorDomain(Common.NSErrorDomain);
if (auto conformance = Common.SwiftConformance)
Info.setSwiftConformance(conformance);
}
// Translate from Method into ObjCMethodInfo and write it out.
@ -990,8 +996,6 @@ public:
TI.SwiftRetainOp = T.SwiftRetainOp;
if (T.SwiftReleaseOp)
TI.SwiftReleaseOp = T.SwiftReleaseOp;
if (T.SwiftConformance)
TI.SwiftConformance = T.SwiftConformance;
if (T.SwiftDefaultOwnership)
TI.SwiftDefaultOwnership = T.SwiftDefaultOwnership;

View File

@ -336,6 +336,10 @@ static void ProcessAPINotes(Sema &S, Decl *D,
});
}
if (auto ConformsTo = Info.getSwiftConformance())
D->addAttr(
SwiftAttrAttr::Create(S.Context, "conforms_to:" + ConformsTo.value()));
ProcessAPINotes(S, D, static_cast<const api_notes::CommonEntityInfo &>(Info),
Metadata);
}
@ -698,10 +702,6 @@ static void ProcessAPINotes(Sema &S, TagDecl *D, const api_notes::TagInfo &Info,
D->addAttr(SwiftAttrAttr::Create(
S.Context, "returned_as_" + DefaultOwnership.value() + "_by_default"));
if (auto ConformsTo = Info.SwiftConformance)
D->addAttr(
SwiftAttrAttr::Create(S.Context, "conforms_to:" + ConformsTo.value()));
if (auto Copyable = Info.isSwiftCopyable()) {
if (!*Copyable)
D->addAttr(SwiftAttrAttr::Create(S.Context, "~Copyable"));

View File

@ -26,3 +26,6 @@ Classes:
- Name: scalarNewProperty
PropertyKind: Instance
Nullability: Scalar
Typedefs:
- Name: MyTypedef
SwiftConformsTo: Swift.Equatable

View File

@ -39,3 +39,7 @@ Functions:
SwiftReturnOwnership: unretained
- Name: functionReturningFrt_returns_retained
SwiftReturnOwnership: retained
Typedefs:
- Name: WrappedOptions
SwiftWrapper: struct
SwiftConformsTo: Swift.OptionSet

View File

@ -29,3 +29,5 @@ struct OpaqueRefCountedType; // redeclaration
inline void ORCRetain(struct OpaqueRefCountedType *x);
inline void ORCRelease(struct OpaqueRefCountedType *x);
typedef unsigned WrappedOptions;

View File

@ -14,6 +14,7 @@
// 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 methodReturningFrt__ | FileCheck -check-prefix=CHECK-METHOD-RETURNING-FRT %s
// 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 methodReturningFrt_returns_unretained | FileCheck -check-prefix=CHECK-METHOD-RETURNING-FRT-UNRETAINED %s
// 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 methodReturningFrt_returns_retained | FileCheck -check-prefix=CHECK-METHOD-RETURNING-FRT-RETAINED %s
// 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 WrappedOptions | FileCheck -check-prefix=CHECK-WRAPPED-OPTIONS %s
#include <SwiftImportAs.h>
@ -51,8 +52,8 @@
// CHECK-OPAQUE-REF-COUNTED-NOT: SwiftAttrAttr {{.+}} <<invalid sloc>> "release:
// CHECK-NON-COPYABLE: Dumping NonCopyableType:
// CHECK-NON-COPYABLE-NEXT: CXXRecordDecl {{.+}} imported in SwiftImportAs {{.+}} struct NonCopyableType
// CHECK-NON-COPYABLE: SwiftAttrAttr {{.+}} <<invalid sloc>> "conforms_to:MySwiftModule.MySwiftNonCopyableProtocol"
// CHECK-NON-COPYABLE: SwiftAttrAttr {{.+}} <<invalid sloc>> "~Copyable"
// CHECK-NON-COPYABLE: SwiftAttrAttr {{.+}} <<invalid sloc>> "conforms_to:MySwiftModule.MySwiftNonCopyableProtocol"
// CHECK-COPYABLE: Dumping CopyableType:
// CHECK-COPYABLE-NEXT: CXXRecordDecl {{.+}} imported in SwiftImportAs {{.+}} struct CopyableType
@ -91,3 +92,8 @@
// CHECK-METHOD-RETURNING-FRT-RETAINED: Dumping ImmortalRefType::methodReturningFrt_returns_retained:
// CHECK-METHOD-RETURNING-FRT-RETAINED: CXXMethodDecl {{.+}} imported in SwiftImportAs methodReturningFrt_returns_retained 'ImmortalRefType *()'
// CHECK-METHOD-RETURNING-FRT-RETAINED: `-SwiftAttrAttr {{.+}} "returns_retained"
// CHECK-WRAPPED-OPTIONS: Dumping WrappedOptions
// CHECK-WRAPPED-OPTIONS: TypedefDecl{{.*}}WrappedOptions 'unsigned int'
// CHECK-WRAPPED-OPTIONS: SwiftNewTypeAttr {{.*}} swift_wrapper NK_Struct
// CHECK-WRAPPED-OPTIONS: SwiftAttrAttr {{.*}} "conforms_to:Swift.OptionSet"

View File

@ -24,7 +24,7 @@ CHECK-NEXT: 25c26
CHECK-NEXT: < Nullability: S
CHECK-NEXT: ---
CHECK-NEXT: > Nullability: Unspecified
CHECK-NEXT: 28c29,30
CHECK-NEXT: 28c29
CHECK-NEXT: < Nullability: Scalar
CHECK-NEXT: ---
CHECK-NEXT: > Nullability: Unspecified