Skip to content

Commit 7f2fff6

Browse files
authored
Merge pull request #18739 from nkcsgexi/abi-flag
swift-api-digester: diagnose the addition and removal of @objc, @_fixed_layout and @_frozen under ABI mode.
2 parents c8f8afd + 17b9a4e commit 7f2fff6

File tree

7 files changed

+138
-18
lines changed

7 files changed

+138
-18
lines changed

include/swift/IDE/DigesterEnums.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,9 @@ NODE_ANNOTATION(NowThrowing)
5959
NODE_ANNOTATION(NowMutating)
6060
NODE_ANNOTATION(StaticChange)
6161
NODE_ANNOTATION(OwnershipChange)
62+
NODE_ANNOTATION(ChangeObjC)
63+
NODE_ANNOTATION(ChangeFixedLayout)
64+
NODE_ANNOTATION(ChangeFrozen)
6265
NODE_ANNOTATION(RawTypeLeft)
6366
NODE_ANNOTATION(RawTypeRight)
6467

test/api-digester/Inputs/cake1.swift

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,12 @@ public struct Somestruct2 {
2727

2828
public class C4: OldType {
2929
public func foo() {}
30-
}
30+
}
31+
32+
@objc
33+
public class C5 {}
34+
35+
public struct C6 {}
36+
37+
@_frozen
38+
public enum IceKind {}

test/api-digester/Inputs/cake2.swift

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,4 +28,11 @@ public struct NSSomestruct2 {
2828
public static func foo1(_ a : C3) {}
2929
}
3030

31-
public class C4: NewType {}
31+
public class C4: NewType {}
32+
33+
public class C5 {}
34+
35+
@_fixed_layout
36+
public struct C6 {}
37+
38+
public enum IceKind {}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
2+
/* RawRepresentable Changes */
3+
4+
/* Removed Decls */
5+
cake1: Constructor Somestruct2.init(_:) has been removed
6+
cake1: Func C4.foo() has been removed
7+
8+
/* Moved Decls */
9+
10+
/* Renamed Decls */
11+
cake1: Struct Somestruct2 has been renamed to Struct NSSomestruct2
12+
cake1: Func S1.foo5(x:y:) has been renamed to Func S1.foo5(x:y:z:)
13+
14+
/* Type Changes */
15+
cake1: Constructor S1.init(_:) has parameter 0 type change from Int to Double
16+
cake1: Func C1.foo2(_:) has parameter 0 type change from Int to () -> ()
17+
cake1: Func Somestruct2.foo1(_:) has parameter 0 type change from C3 to C1
18+
19+
/* Decl Attribute changes */
20+
cake1: Enum IceKind is now without @_frozen
21+
cake1: Struct C6 is now with @_fixed_layout
22+
cake1: Class C5 is now without @objc
23+
cake1: Var C1.CIIns1 changes from weak to strong
24+
cake1: Var C1.CIIns2 changes from strong to weak
25+
cake1: Func C1.foo1() is now not static
26+
cake1: Func S1.foo1() is now mutating
27+
cake1: Func S1.foo3() is now static

test/api-digester/Outputs/Cake.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,5 @@ cake1: Func C1.foo2(_:) has parameter 0 type change from Int to () -> ()
1919
cake1: Var C1.CIIns1 changes from weak to strong
2020
cake1: Var C1.CIIns2 changes from strong to weak
2121
cake1: Func C1.foo1() is now not static
22-
cake1: Func S1.foo3() is now static
2322
cake1: Func S1.foo1() is now mutating
23+
cake1: Func S1.foo3() is now static

test/api-digester/compare-dump.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,8 @@
77
// RUN: %api-digester -dump-sdk -module cake2 -o %t.dump2.json -module-cache-path %t.module-cache %clang-importer-sdk-nosource -I %t.mod -I %S/Inputs/APINotesRight
88
// RUN: %api-digester -diagnose-sdk -print-module --input-paths %t.dump1.json -input-paths %t.dump2.json > %t.result
99
// RUN: diff -u %S/Outputs/Cake.txt %t.result
10+
11+
// RUN: %api-digester -dump-sdk -module cake1 -o %t.dump1.json -module-cache-path %t.module-cache %clang-importer-sdk-nosource -I %t.mod -I %S/Inputs/APINotesLeft -abi
12+
// RUN: %api-digester -dump-sdk -module cake2 -o %t.dump2.json -module-cache-path %t.module-cache %clang-importer-sdk-nosource -I %t.mod -I %S/Inputs/APINotesRight -abi
13+
// RUN: %api-digester -diagnose-sdk -print-module --input-paths %t.dump1.json -input-paths %t.dump2.json -abi > %t.result
14+
// RUN: diff -u %S/Outputs/Cake-abi.txt %t.result

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

Lines changed: 85 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -252,16 +252,43 @@ class UpdatedNodesMap : public MatchedNodeListener {
252252
}
253253
};
254254

255+
// Describing some attributes with ABI impact. The addition or removal of these
256+
// attributes is considerred ABI-breaking.
257+
struct ABIAttributeInfo {
258+
const DeclAttrKind Kind;
259+
const NodeAnnotation Annotation;
260+
const StringRef Content;
261+
};
262+
255263
class SDKContext {
256-
bool ABI;
257264
llvm::StringSet<> TextData;
258265
llvm::BumpPtrAllocator Allocator;
259266
UpdatedNodesMap UpdateMap;
260267
NodeMap TypeAliasUpdateMap;
261268
NodeMap RevertTypeAliasUpdateMap;
262269
TypeMemberDiffVector TypeMemberDiffs;
270+
271+
bool ABI;
272+
std::vector<ABIAttributeInfo> ABIAttrs;
273+
274+
static StringRef getAttrName(DeclAttrKind Kind) {
275+
switch (Kind) {
276+
#define DECL_ATTR(NAME, CLASS, ...) case DAK_##CLASS: return "@"#NAME;
277+
#include "swift/AST/Attr.def"
278+
case DAK_Count:
279+
llvm_unreachable("unrecognized attribute kind.");
280+
}
281+
}
282+
263283
public:
264-
SDKContext(bool ABI): ABI(ABI) {}
284+
SDKContext(bool ABI): ABI(ABI) {
285+
#define ADD(NAME) ABIAttrs.push_back({DeclAttrKind::DAK_##NAME, \
286+
NodeAnnotation::Change##NAME, getAttrName(DeclAttrKind::DAK_##NAME)});
287+
ADD(ObjC)
288+
ADD(FixedLayout)
289+
ADD(Frozen)
290+
#undef ADD
291+
}
265292
llvm::BumpPtrAllocator &allocator() {
266293
return Allocator;
267294
}
@@ -281,6 +308,7 @@ class SDKContext {
281308
return TypeMemberDiffs;
282309
}
283310
bool checkingABI() const { return ABI; }
311+
ArrayRef<ABIAttributeInfo> getABIAttributeInfo() const { return ABIAttrs; }
284312
};
285313

286314
// A node matcher will traverse two trees of SDKNode and find matched nodes
@@ -446,7 +474,6 @@ class SDKNodeDecl : public SDKNode {
446474
bool IsStatic;
447475
bool IsDeprecated;
448476
uint8_t ReferenceOwnership;
449-
bool hasDeclAttribute(DeclAttrKind DAKind) const;
450477
StringRef GenericSig;
451478

452479
protected:
@@ -464,6 +491,7 @@ class SDKNodeDecl : public SDKNode {
464491
StringRef getModuleName() const {return ModuleName;}
465492
StringRef getHeaderName() const;
466493
ArrayRef<DeclAttrKind> getDeclAttributes() const;
494+
bool hasAttributeChange(const SDKNodeDecl &Another) const;
467495
swift::ReferenceOwnership getReferenceOwnership() const {
468496
return swift::ReferenceOwnership(ReferenceOwnership);
469497
}
@@ -474,7 +502,7 @@ class SDKNodeDecl : public SDKNode {
474502
StringRef getFullyQualifiedName() const;
475503
bool isSDKPrivate() const;
476504
bool isDeprecated() const { return IsDeprecated; };
477-
bool hasFixedLayout() const;
505+
bool hasDeclAttribute(DeclAttrKind DAKind) const;
478506
bool isStatic() const { return IsStatic; };
479507
StringRef getGenericSignature() const { return GenericSig; }
480508
};
@@ -761,10 +789,6 @@ SDKNode *SDKNodeRoot::getInstance(SDKContext &Ctx) {
761789
return Info.createSDKNode(SDKNodeKind::Root);
762790
}
763791

764-
bool SDKNodeDecl::hasFixedLayout() const {
765-
return hasDeclAttribute(DeclAttrKind::DAK_FixedLayout);
766-
}
767-
768792
bool SDKNodeDecl::isSDKPrivate() const {
769793
if (getName().startswith("__"))
770794
return true;
@@ -818,6 +842,16 @@ ArrayRef<DeclAttrKind> SDKNodeDecl::getDeclAttributes() const {
818842
return llvm::makeArrayRef(DeclAttributes.data(), DeclAttributes.size());
819843
}
820844

845+
bool SDKNodeDecl::hasAttributeChange(const SDKNodeDecl &Another) const {
846+
if (getDeclAttributes().size() != Another.getDeclAttributes().size())
847+
return true;
848+
for (auto K: getDeclAttributes()) {
849+
if (!Another.hasDeclAttribute(K))
850+
return true;
851+
}
852+
return false;
853+
}
854+
821855
SDKNodeDecl *SDKNodeType::getClosestParentDecl() const {
822856
auto *Result = getParent();
823857
for (; !isa<SDKNodeDecl>(Result); Result = Result->getParent());
@@ -1175,6 +1209,10 @@ bool SDKNode::operator==(const SDKNode &Other) const {
11751209
return false;
11761210
if (Left->getReferenceOwnership() != Right->getReferenceOwnership())
11771211
return false;
1212+
if (Left->hasAttributeChange(*Right))
1213+
return false;
1214+
if (Left->getGenericSignature() != Right->getGenericSignature())
1215+
return false;
11781216
LLVM_FALLTHROUGH;
11791217
}
11801218
case SDKNodeKind::Root: {
@@ -2454,13 +2492,19 @@ static bool isOwnershipEquivalent(ReferenceOwnership Left,
24542492

24552493
static void detectDeclChange(NodePtr L, NodePtr R) {
24562494
assert(L->getKind() == R->getKind());
2495+
auto &Ctx = L->getSDKContext();
24572496
if (auto LD = dyn_cast<SDKNodeDecl>(L)) {
24582497
auto *RD = R->getAs<SDKNodeDecl>();
24592498
if (LD->isStatic() ^ RD->isStatic())
24602499
L->annotate(NodeAnnotation::StaticChange);
24612500
if (!isOwnershipEquivalent(LD->getReferenceOwnership(),
24622501
RD->getReferenceOwnership()))
24632502
L->annotate(NodeAnnotation::OwnershipChange);
2503+
// Check if some attributes with ABI-impact have been added/removed.
2504+
for (auto &Info: Ctx.getABIAttributeInfo()) {
2505+
if (LD->hasDeclAttribute(Info.Kind) != RD->hasDeclAttribute(Info.Kind))
2506+
L->annotate(Info.Annotation);
2507+
}
24642508
detectRename(L, R);
24652509
}
24662510
}
@@ -3266,6 +3310,8 @@ class DiagnosisEmitter : public SDKNodeVisitor {
32663310
llvm::outs() << " */\n";
32673311
removeRedundantAndSort(Diags);
32683312
std::for_each(Diags.begin(), Diags.end(), [](T &Diag) {
3313+
if (Diag.isABISpecific() && !options::Abi)
3314+
return;
32693315
Diag.outputModule();
32703316
Diag.output();
32713317
});
@@ -3275,12 +3321,15 @@ class DiagnosisEmitter : public SDKNodeVisitor {
32753321
struct MetaInfo {
32763322
StringRef ModuleName;
32773323
StringRef HeaderName;
3278-
MetaInfo(const SDKNodeDecl *Node):
3279-
ModuleName(Node->getModuleName()), HeaderName(Node->getHeaderName()) {}
3324+
bool IsABISpecific;
3325+
MetaInfo(const SDKNodeDecl *Node, bool IsABISpecific = false):
3326+
ModuleName(Node->getModuleName()), HeaderName(Node->getHeaderName()),
3327+
IsABISpecific(IsABISpecific) {}
32803328
};
32813329

3282-
struct DiagBase {
3330+
class DiagBase {
32833331
MetaInfo Info;
3332+
public:
32843333
DiagBase(MetaInfo Info): Info(Info) {}
32853334
virtual ~DiagBase() = default;
32863335
void outputModule() const {
@@ -3292,6 +3341,7 @@ class DiagnosisEmitter : public SDKNodeVisitor {
32923341
}
32933342
}
32943343
virtual void output() const = 0;
3344+
bool isABISpecific() const { return Info.IsABISpecific; }
32953345
};
32963346

32973347
struct RemovedDeclDiag: public DiagBase {
@@ -3513,7 +3563,9 @@ void DiagnosisEmitter::DeclTypeChangeDiag::output() const {
35133563
bool DiagnosisEmitter::DeclAttrDiag::operator<(DeclAttrDiag Other) const {
35143564
if (Kind != Other.Kind)
35153565
return Kind < Other.Kind;
3516-
return DeclName.compare_lower(Other.DeclName);
3566+
if (DeclName != Other.DeclName)
3567+
return DeclName.compare(Other.DeclName) < 0;
3568+
return AttrAfter.compare(Other.AttrAfter) < 0;
35173569
}
35183570

35193571
void DiagnosisEmitter::DeclAttrDiag::output() const {
@@ -3535,9 +3587,9 @@ void DiagnosisEmitter::diagnosis(NodePtr LeftRoot, NodePtr RightRoot,
35353587
void DiagnosisEmitter::handle(const SDKNodeDecl *Node, NodeAnnotation Anno) {
35363588
assert(Node->isAnnotatedAs(Anno));
35373589
auto &Ctx = Node->getSDKContext();
3538-
MetaInfo ScreenInfo(Node);
35393590
switch(Anno) {
35403591
case NodeAnnotation::Removed: {
3592+
MetaInfo ScreenInfo(Node, false);
35413593
// If we can find a type alias decl with the same name of this type, we
35423594
// consider the type is not removed.
35433595
if (findTypeAliasDecl(Node))
@@ -3599,6 +3651,7 @@ void DiagnosisEmitter::handle(const SDKNodeDecl *Node, NodeAnnotation Anno) {
35993651
return;
36003652
}
36013653
case NodeAnnotation::Rename: {
3654+
MetaInfo ScreenInfo(Node, false);
36023655
auto *Count = UpdateMap.findUpdateCounterpart(Node)->getAs<SDKNodeDecl>();
36033656
RenamedDecls.Diags.emplace_back(ScreenInfo,
36043657
Node->getDeclKind(), Count->getDeclKind(),
@@ -3607,27 +3660,31 @@ void DiagnosisEmitter::handle(const SDKNodeDecl *Node, NodeAnnotation Anno) {
36073660
return;
36083661
}
36093662
case NodeAnnotation::NowMutating: {
3663+
MetaInfo ScreenInfo(Node, false);
36103664
AttrChangedDecls.Diags.emplace_back(ScreenInfo,
36113665
Node->getDeclKind(),
36123666
Node->getFullyQualifiedName(),
36133667
Ctx.buffer("mutating"));
36143668
return;
36153669
}
36163670
case NodeAnnotation::NowThrowing: {
3671+
MetaInfo ScreenInfo(Node, false);
36173672
AttrChangedDecls.Diags.emplace_back(ScreenInfo,
36183673
Node->getDeclKind(),
36193674
Node->getFullyQualifiedName(),
36203675
Ctx.buffer("throwing"));
36213676
return;
36223677
}
36233678
case NodeAnnotation::StaticChange: {
3679+
MetaInfo ScreenInfo(Node, false);
36243680
AttrChangedDecls.Diags.emplace_back(ScreenInfo,
36253681
Node->getDeclKind(),
36263682
Node->getFullyQualifiedName(),
36273683
Ctx.buffer(Node->isStatic() ? "not static" : "static"));
36283684
return;
36293685
}
36303686
case NodeAnnotation::OwnershipChange: {
3687+
MetaInfo ScreenInfo(Node, false);
36313688
auto getOwnershipDescription = [&](swift::ReferenceOwnership O) {
36323689
if (O == ReferenceOwnership::Strong)
36333690
return Ctx.buffer("strong");
@@ -3640,9 +3697,22 @@ void DiagnosisEmitter::handle(const SDKNodeDecl *Node, NodeAnnotation Anno) {
36403697
getOwnershipDescription(Count->getReferenceOwnership()));
36413698
return;
36423699
}
3643-
default:
3700+
default: {
3701+
// Diagnose the addition/removal of attributes with ABI impact.
3702+
auto Infos = Ctx.getABIAttributeInfo();
3703+
auto It = std::find_if(Infos.begin(), Infos.end(),
3704+
[&](const ABIAttributeInfo &I) { return I.Annotation == Anno; });
3705+
if (It == Infos.end())
3706+
return;
3707+
MetaInfo ScreenInfo(Node, true);
3708+
auto Desc = Node->hasDeclAttribute(It->Kind) ?
3709+
Ctx.buffer((llvm::Twine("without ") + It->Content).str()):
3710+
Ctx.buffer((llvm::Twine("with ") + It->Content).str());
3711+
AttrChangedDecls.Diags.emplace_back(ScreenInfo, Node->getDeclKind(),
3712+
Node->getFullyQualifiedName(), Desc);
36443713
return;
36453714
}
3715+
}
36463716
}
36473717

36483718
void DiagnosisEmitter::visitDecl(SDKNodeDecl *Node) {
@@ -3692,7 +3762,7 @@ void DiagnosisEmitter::visit(NodePtr Node) {
36923762
}
36933763
}
36943764

3695-
typedef std::vector<NoEscapeFuncParam> NoEscapeFuncParamVector;
3765+
typedef std::vector<NoEscapeFuncParam> NoEscapeFuncParamVector;
36963766

36973767
class NoEscapingFuncEmitter : public SDKNodeVisitor {
36983768
NoEscapeFuncParamVector &AllItems;

0 commit comments

Comments
 (0)