Skip to content

Commit 1aa7816

Browse files
committed
swift-api-digester: teach the tool to serialize and de-serialize generic requirements.
The tool should diagnose the change of extension's applicability since such change can be source-breaking. We need first to support the requirements in the module dump. Currently, we decorate each member defined in extension with a field called extension info. The field will keep track of the generic requirements that need to be satisfied for this decorated member to be applicable. This patch doesn't implement the checking of requirements change.
1 parent 75e90ca commit 1aa7816

File tree

5 files changed

+167
-8
lines changed

5 files changed

+167
-8
lines changed

include/swift/IDE/DigesterEnums.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ KEY(declAttributes)
8989
KEY(declKind)
9090
KEY(ownership)
9191
KEY(superclassUsr)
92+
KEY(parentExtensionReqs)
9293

9394
KNOWN_TYPE(Optional)
9495
KNOWN_TYPE(ImplicitlyUnwrappedOptional)

test/api-digester/Inputs/cake.swift

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,18 @@ public struct S1 {
77
public func foo6() -> Void {}
88
}
99

10-
public class C0 {}
10+
public class C0<T1, T2, T3> {}
1111

12-
public class C1: C0 {
12+
public class C1: C0<S1, S1, S1> {
1313
open class func foo1() {}
1414
public weak var Ins : C1?
1515
public unowned var Ins2 : C1 = C1()
1616
}
17+
18+
public extension C0 where T1 == S1, T2 == S1, T3 == S1 {
19+
func conditionalFooExt() {}
20+
}
21+
22+
public extension C0 {
23+
func unconditionalFooExt() {}
24+
}

test/api-digester/Outputs/cake.json

Lines changed: 57 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,69 @@
1717
"name": "init",
1818
"printedName": "init()",
1919
"declKind": "Constructor",
20-
"usr": "s:4cake2C0CACycfc",
20+
"usr": "s:4cake2C0CACyxq_q0_Gycfc",
2121
"location": "",
2222
"moduleName": "cake",
2323
"children": [
2424
{
2525
"kind": "TypeNominal",
2626
"name": "C0",
27-
"printedName": "C0"
27+
"printedName": "C0<T1, T2, T3>",
28+
"children": [
29+
{
30+
"kind": "TypeNominal",
31+
"name": "GenericTypeParam",
32+
"printedName": "T1"
33+
},
34+
{
35+
"kind": "TypeNominal",
36+
"name": "GenericTypeParam",
37+
"printedName": "T2"
38+
},
39+
{
40+
"kind": "TypeNominal",
41+
"name": "GenericTypeParam",
42+
"printedName": "T3"
43+
}
44+
]
45+
}
46+
]
47+
},
48+
{
49+
"kind": "Function",
50+
"name": "conditionalFooExt",
51+
"printedName": "conditionalFooExt()",
52+
"declKind": "Func",
53+
"usr": "s:4cake2C0CA2A2S1VRszAERs_AERs0_rlE17conditionalFooExtyyF",
54+
"location": "",
55+
"moduleName": "cake",
56+
"parentExtensionReqs": [
57+
"T1 == cake.S1",
58+
"T2 == cake.S1",
59+
"T3 == cake.S1"
60+
],
61+
"children": [
62+
{
63+
"kind": "TypeNominal",
64+
"name": "Void",
65+
"printedName": "()"
66+
}
67+
]
68+
},
69+
{
70+
"kind": "Function",
71+
"name": "unconditionalFooExt",
72+
"printedName": "unconditionalFooExt()",
73+
"declKind": "Func",
74+
"usr": "s:4cake2C0C19unconditionalFooExtyyF",
75+
"location": "",
76+
"moduleName": "cake",
77+
"parentExtensionReqs": [],
78+
"children": [
79+
{
80+
"kind": "TypeNominal",
81+
"name": "Void",
82+
"printedName": "()"
2883
}
2984
]
3085
}

test/api-digester/dump-module.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,7 @@
55
// RUN: %api-digester -dump-sdk -module cake -o %t.dump.json -module-cache-path %t.module-cache -sdk %t.sdk -swift-version 3 -I %t.mod
66
// RUN: diff -u %S/Outputs/cake.json %t.dump.json
77
// RUN: %api-digester -diagnose-sdk --input-paths %t.dump.json -input-paths %S/Outputs/cake.json
8+
9+
// Round-trip testing:
10+
// RUN: %api-digester -deserialize-sdk --input-paths %S/Outputs/cake.json -o %t.dump.json
11+
// RUN: diff -u %S/Outputs/cake.json %t.dump.json

tools/swift-api-digester/swift-api-digester.cpp

Lines changed: 95 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,9 @@ namespace {
6767
DumpSwiftModules,
6868
CompareSDKs,
6969
DiagnoseSDKs,
70+
// The following two are for testing purposes
7071
DeserializeDiffItems,
72+
DeserializeSDK,
7173
};
7274
} // end anonymous namespace
7375

@@ -135,7 +137,10 @@ Action(llvm::cl::desc("Mode:"), llvm::cl::init(ActionType::None),
135137
"Diagnose SDK content in JSON file"),
136138
clEnumValN(ActionType::DeserializeDiffItems,
137139
"deserialize-diff",
138-
"Deserialize diff items in a JSON file")));
140+
"Deserialize diff items in a JSON file"),
141+
clEnumValN(ActionType::DeserializeSDK,
142+
"deserialize-sdk",
143+
"Deserialize sdk digester in a JSON file")));
139144

140145
static llvm::cl::list<std::string>
141146
SDKJsonPaths("input-paths",
@@ -287,6 +292,22 @@ static raw_ostream &operator<<(raw_ostream &Out, const DeclKind Value) {
287292
llvm_unreachable("Unhandled DeclKind in switch.");
288293
}
289294

295+
/// We don't dump individual extension declaration in the digester. However,
296+
/// we still want to detect whether an extension's applicability changes. Therefore,
297+
/// by using ParentExtensionInfo, we keep track of extension's information in
298+
/// each member of the extension.
299+
class ParentExtensionInfo {
300+
friend struct SDKNodeInitInfo;
301+
friend class SDKNode;
302+
std::vector<StringRef> Requirements;
303+
304+
void *operator new(size_t Bytes, SDKContext &C) {
305+
return C.allocator().Allocate<ParentExtensionInfo>();
306+
}
307+
public:
308+
ArrayRef<StringRef> getGenericRequirements() const { return Requirements; }
309+
};
310+
290311
struct SDKNodeInitInfo {
291312
SDKContext &Ctx;
292313
StringRef Name;
@@ -303,6 +324,8 @@ struct SDKNodeInitInfo {
303324
std::vector<SDKDeclAttrKind> DeclAttrs;
304325
std::vector<TypeAttrKind> TypeAttrs;
305326
StringRef SuperclassUsr;
327+
ParentExtensionInfo *ExtInfo = nullptr;
328+
306329
SDKNodeInitInfo(SDKContext &Ctx) : Ctx(Ctx) {}
307330
SDKNodeInitInfo(SDKContext &Ctx, ValueDecl *VD);
308331
SDKNodeInitInfo(SDKContext &Ctx, Type Ty,
@@ -372,12 +395,15 @@ class SDKNodeDecl : public SDKNode {
372395
bool IsStatic;
373396
uint8_t Ownership;
374397
bool hasDeclAttribute(SDKDeclAttrKind DAKind) const;
398+
// Non-null ExtInfo implies this decl is defined in an type extension.
399+
ParentExtensionInfo *ExtInfo;
375400

376401
protected:
377402
SDKNodeDecl(SDKNodeInitInfo Info, SDKNodeKind Kind) : SDKNode(Info, Kind),
378403
DKind(Info.DKind), Usr(Info.USR), Location(Info.Location),
379404
ModuleName(Info.ModuleName), DeclAttributes(Info.DeclAttrs),
380-
IsStatic(Info.IsStatic), Ownership(uint8_t(Info.Ownership)) {}
405+
IsStatic(Info.IsStatic), Ownership(uint8_t(Info.Ownership)),
406+
ExtInfo(Info.ExtInfo) {}
381407

382408
public:
383409
StringRef getUsr() const { return Usr; }
@@ -395,6 +421,11 @@ class SDKNodeDecl : public SDKNode {
395421
bool isSDKPrivate() const;
396422
bool isDeprecated() const;
397423
bool isStatic() const { return IsStatic; };
424+
bool isFromExtension() const { return ExtInfo; }
425+
const ParentExtensionInfo& getExtensionInfo() const {
426+
assert(isFromExtension());
427+
return *ExtInfo;
428+
}
398429
};
399430

400431
StringRef SDKNodeDecl::getHeaderName() const {
@@ -893,6 +924,14 @@ SDKNode* SDKNode::constructSDKNode(SDKContext &Ctx,
893924
cast<llvm::yaml::MappingNode>(&Mapping)));
894925
}
895926
break;
927+
case KeyKind::KK_parentExtensionReqs: {
928+
assert(!Info.ExtInfo);
929+
Info.ExtInfo = new (Ctx) ParentExtensionInfo();
930+
for (auto &Req : *cast<llvm::yaml::SequenceNode>(Pair.getValue())) {
931+
Info.ExtInfo->Requirements.push_back(GetScalarString(&Req));
932+
}
933+
break;
934+
}
896935
case KeyKind::KK_printedName:
897936
Info.PrintedName = GetScalarString(Pair.getValue());
898937
break;
@@ -1194,7 +1233,7 @@ SDKNodeInitInfo::SDKNodeInitInfo(SDKContext &Ctx, ValueDecl *VD) : Ctx(Ctx),
11941233
ModuleName(VD->getModuleContext()->getName().str()),
11951234
IsThrowing(isFuncThrowing(VD)), IsMutating(isFuncMutating(VD)),
11961235
IsStatic(VD->isStatic()), SelfIndex(getSelfIndex(VD)),
1197-
Ownership(getOwnership(VD)) {
1236+
Ownership(getOwnership(VD)), ExtInfo(nullptr) {
11981237

11991238
// Calculate usr for its super class.
12001239
if (auto *CD = dyn_cast_or_null<ClassDecl>(VD)) {
@@ -1203,6 +1242,18 @@ SDKNodeInitInfo::SDKNodeInitInfo(SDKContext &Ctx, ValueDecl *VD) : Ctx(Ctx),
12031242
}
12041243
if (VD->getAttrs().getDeprecated(VD->getASTContext()))
12051244
DeclAttrs.push_back(SDKDeclAttrKind::DAK_deprecated);
1245+
1246+
// If the decl is declared in an extension, calculate the extension info.
1247+
if (auto *Ext = dyn_cast_or_null<ExtensionDecl>(VD->getDeclContext())) {
1248+
ExtInfo = new (Ctx) ParentExtensionInfo();
1249+
// Print each generic requirement to the extension info.
1250+
for (auto Req: Ext->getGenericRequirements()) {
1251+
llvm::SmallString<32> Result;
1252+
llvm::raw_svector_ostream OS(Result);
1253+
Req.print(OS, PrintOptions::printInterface());
1254+
ExtInfo->Requirements.emplace_back(Ctx.buffer(OS.str()));
1255+
}
1256+
}
12061257
}
12071258

12081259
SDKNode *SDKNodeInitInfo::createSDKNode(SDKNodeKind Kind) {
@@ -1576,6 +1627,15 @@ namespace swift {
15761627
Super);
15771628
}
15781629
}
1630+
if (D->isFromExtension()) {
1631+
// Even if we don't have any requirements on this parent extension,
1632+
// we still want to have this key present to indicate the member
1633+
// is from an extension.
1634+
auto Reqs = D->getExtensionInfo().getGenericRequirements();
1635+
out.mapRequired(getKeyContent(Ctx,
1636+
KeyKind::KK_parentExtensionReqs).data(),
1637+
Reqs);
1638+
}
15791639
auto Attributes = D->getDeclAttributes();
15801640
if (!Attributes.empty())
15811641
out.mapRequired(getKeyContent(Ctx, KeyKind::KK_declAttributes).data(),
@@ -1630,6 +1690,16 @@ namespace swift {
16301690
return const_cast<SDKDeclAttrKind&>(seq[index]);
16311691
}
16321692
};
1693+
template<>
1694+
struct ArrayTraits<ArrayRef<StringRef>> {
1695+
static size_t size(Output &out, ArrayRef<StringRef> &seq) {
1696+
return seq.size();
1697+
}
1698+
static StringRef& element(Output &, ArrayRef<StringRef> &seq,
1699+
size_t index) {
1700+
return const_cast<StringRef&>(seq[index]);
1701+
}
1702+
};
16331703
} // namespace json
16341704
} // namespace swift
16351705

@@ -3497,6 +3567,23 @@ static int deserializeDiffItems(StringRef DiffPath, StringRef OutputPath) {
34973567
return 0;
34983568
}
34993569

3570+
/// Mostly for testing purposes, this function de-serializes the SDK dump in
3571+
/// dumpPath and re-serialize them to OutputPath. If the tool performs correctly,
3572+
/// the contents in dumpPath and OutputPath should be identical.
3573+
static int deserializeSDKDump(StringRef dumpPath, StringRef OutputPath) {
3574+
std::error_code EC;
3575+
llvm::raw_fd_ostream FS(OutputPath, EC, llvm::sys::fs::F_None);
3576+
if (!fs::exists(dumpPath)) {
3577+
llvm::errs() << dumpPath << " does not exist\n";
3578+
return 1;
3579+
}
3580+
SDKContext Ctx;
3581+
SwiftDeclCollector Collector(Ctx);
3582+
Collector.deSerialize(dumpPath);
3583+
Collector.serialize(OutputPath);
3584+
return 0;
3585+
}
3586+
35003587
int main(int argc, char *argv[]) {
35013588
INITIALIZE_LLVM(argc, argv);
35023589

@@ -3532,12 +3619,16 @@ int main(int argc, char *argv[]) {
35323619
else
35333620
return diagnoseModuleChange(options::SDKJsonPaths[0],
35343621
options::SDKJsonPaths[1]);
3622+
case ActionType::DeserializeSDK:
35353623
case ActionType::DeserializeDiffItems: {
35363624
if (options::SDKJsonPaths.size() != 1) {
35373625
llvm::cl::PrintHelpMessage();
35383626
return 1;
35393627
}
3540-
return deserializeDiffItems(options::SDKJsonPaths[0], options::OutputFile);
3628+
if (options::Action == ActionType::DeserializeDiffItems)
3629+
return deserializeDiffItems(options::SDKJsonPaths[0], options::OutputFile);
3630+
else
3631+
return deserializeSDKDump(options::SDKJsonPaths[0], options::OutputFile);
35413632
}
35423633
case ActionType::None:
35433634
llvm::errs() << "Action required\n";

0 commit comments

Comments
 (0)