swift-module-digester: diagnose adding/removing @escaping as ABI breakage.

This commit is contained in:
Xi Ge
2018-09-27 17:42:55 -07:00
parent 6665b56e9d
commit 7b45ae9135
8 changed files with 91 additions and 47 deletions

View File

@@ -80,6 +80,8 @@ ERROR(var_let_changed,none,"%0 changes from %select{var|let}1 to %select{let|var
ERROR(no_longer_open,none,"%0 is no longer open for subclassing", (StringRef)) ERROR(no_longer_open,none,"%0 is no longer open for subclassing", (StringRef))
ERROR(func_type_escaping_changed,none,"%0 has %select{removed|added}2 @escaping in %1", (StringRef, StringRef, bool))
#ifndef DIAG_NO_UNDEF #ifndef DIAG_NO_UNDEF
# if defined(DIAG) # if defined(DIAG)
# undef DIAG # undef DIAG

View File

@@ -120,3 +120,8 @@ public class ClassWithOpenMember {
open var property: Int {get { return 1}} open var property: Int {get { return 1}}
open func bar() {} open func bar() {}
} }
public class EscapingFunctionType {
public func removedEscaping(_ a: @escaping ()->()) {}
public func addedEscaping(_ a: ()->()) {}
}

View File

@@ -130,3 +130,8 @@ public class ClassWithOpenMember {
public var property: Int {get { return 1}} public var property: Int {get { return 1}}
public func bar() {} public func bar() {}
} }
public class EscapingFunctionType {
public func removedEscaping(_ a: ()->()) {}
public func addedEscaping(_ a: @escaping ()->()) {}
}

View File

@@ -31,6 +31,8 @@ cake1: Constructor S1.init(_:) has parameter 0 type change from Int to Double
cake1: Func C1.foo2(_:) has parameter 0 type change from Int to () -> () cake1: Func C1.foo2(_:) has parameter 0 type change from Int to () -> ()
cake1: Func C7.foo(_:_:) has removed default argument from parameter 0 cake1: Func C7.foo(_:_:) has removed default argument from parameter 0
cake1: Func C7.foo(_:_:) has removed default argument from parameter 1 cake1: Func C7.foo(_:_:) has removed default argument from parameter 1
cake1: Func EscapingFunctionType.addedEscaping(_:) has added @escaping in parameter 0
cake1: Func EscapingFunctionType.removedEscaping(_:) has removed @escaping in parameter 0
cake1: Func Somestruct2.foo1(_:) has parameter 0 type change from C3 to C1 cake1: Func Somestruct2.foo1(_:) has parameter 0 type change from C3 to C1
/* Decl Attribute changes */ /* Decl Attribute changes */

View File

@@ -352,13 +352,19 @@ ArrayRef<DeclAttrKind> SDKNodeDecl::getDeclAttributes() const {
} }
bool SDKNodeDecl::hasAttributeChange(const SDKNodeDecl &Another) const { bool SDKNodeDecl::hasAttributeChange(const SDKNodeDecl &Another) const {
if (getDeclAttributes().size() != Another.getDeclAttributes().size()) std::set<DeclAttrKind> Left(getDeclAttributes().begin(),
return true; getDeclAttributes().end());
for (auto K: getDeclAttributes()) { std::set<DeclAttrKind> Right(Another.getDeclAttributes().begin(),
if (!Another.hasDeclAttribute(K)) Another.getDeclAttributes().end());
return true; return Left != Right;
} }
return false;
bool SDKNodeType::hasAttributeChange(const SDKNodeType &Another) const {
std::set<TypeAttrKind> Left(getTypeAttributes().begin(),
getTypeAttributes().end());
std::set<TypeAttrKind> Right(Another.getTypeAttributes().begin(),
Another.getTypeAttributes().end());
return Left != Right;
} }
SDKNodeDecl *SDKNodeType::getClosestParentDecl() const { SDKNodeDecl *SDKNodeType::getClosestParentDecl() const {
@@ -614,7 +620,7 @@ bool SDKNode::operator==(const SDKNode &Other) const {
case SDKNodeKind::TypeFunc: { case SDKNodeKind::TypeFunc: {
auto Left = this->getAs<SDKNodeType>(); auto Left = this->getAs<SDKNodeType>();
auto Right = (&Other)->getAs<SDKNodeType>(); auto Right = (&Other)->getAs<SDKNodeType>();
if (!Left->getTypeAttributes().equals(Right->getTypeAttributes())) if (Left->hasAttributeChange(*Right))
return false; return false;
if (Left->hasDefaultArgument() != Right->hasDefaultArgument()) if (Left->hasDefaultArgument() != Right->hasDefaultArgument())
return false; return false;

View File

@@ -348,10 +348,10 @@ class SDKNodeType : public SDKNode {
bool HasDefaultArg; bool HasDefaultArg;
protected: protected:
bool hasTypeAttribute(TypeAttrKind DAKind) const;
SDKNodeType(SDKNodeInitInfo Info, SDKNodeKind Kind); SDKNodeType(SDKNodeInitInfo Info, SDKNodeKind Kind);
~SDKNodeType() = default; ~SDKNodeType() = default;
public: public:
bool hasTypeAttribute(TypeAttrKind DAKind) const;
KnownTypeKind getTypeKind() const; KnownTypeKind getTypeKind() const;
void addTypeAttribute(TypeAttrKind AttrKind); void addTypeAttribute(TypeAttrKind AttrKind);
ArrayRef<TypeAttrKind> getTypeAttributes() const; ArrayRef<TypeAttrKind> getTypeAttributes() const;
@@ -363,6 +363,7 @@ public:
bool isTopLevelType() const { return !isa<SDKNodeType>(getParent()); } bool isTopLevelType() const { return !isa<SDKNodeType>(getParent()); }
static bool classof(const SDKNode *N); static bool classof(const SDKNode *N);
virtual void jsonize(json::Output &Out) override; virtual void jsonize(json::Output &Out) override;
bool hasAttributeChange(const SDKNodeType &Another) const;
}; };
class SDKNodeTypeNominal : public SDKNodeType { class SDKNodeTypeNominal : public SDKNodeType {
@@ -378,7 +379,7 @@ public:
class SDKNodeTypeFunc : public SDKNodeType { class SDKNodeTypeFunc : public SDKNodeType {
public: public:
SDKNodeTypeFunc(SDKNodeInitInfo Info); SDKNodeTypeFunc(SDKNodeInitInfo Info);
bool isEscaping() const { return !hasTypeAttribute(TypeAttrKind::TAK_noescape); } bool isEscaping() const { return hasTypeAttribute(TypeAttrKind::TAK_noescape); }
static bool classof(const SDKNode *N); static bool classof(const SDKNode *N);
}; };

View File

@@ -46,6 +46,7 @@ static StringRef getCategoryName(uint32_t ID) {
return "/* Decl Attribute changes */"; return "/* Decl Attribute changes */";
case LocalDiagID::default_arg_removed: case LocalDiagID::default_arg_removed:
case LocalDiagID::decl_type_change: case LocalDiagID::decl_type_change:
case LocalDiagID::func_type_escaping_changed:
return "/* Type Changes */"; return "/* Type Changes */";
case LocalDiagID::raw_type_change: case LocalDiagID::raw_type_change:
return "/* RawRepresentable Changes */"; return "/* RawRepresentable Changes */";

View File

@@ -811,14 +811,69 @@ static void diagnoseTypeChange(SDKNode* L, SDKNode* R) {
auto *RT = dyn_cast<SDKNodeType>(R); auto *RT = dyn_cast<SDKNodeType>(R);
if (!LT || !RT) if (!LT || !RT)
return; return;
assert(LT->isTopLevelType() == RT->isTopLevelType());
if (!LT->isTopLevelType())
return;
StringRef Descriptor;
auto LParent = cast<SDKNodeDecl>(LT->getParent());
auto RParent = cast<SDKNodeDecl>(RT->getParent());
assert(LParent->getKind() == RParent->getKind());
if (LParent->isSDKPrivate())
return;
switch(LParent->getKind()) {
case SDKNodeKind::Root:
case SDKNodeKind::TypeNominal:
case SDKNodeKind::TypeFunc:
case SDKNodeKind::TypeAlias:
case SDKNodeKind::DeclType:
llvm_unreachable("Type Parent is wrong");
case SDKNodeKind::DeclFunction:
case SDKNodeKind::DeclConstructor:
case SDKNodeKind::DeclGetter:
case SDKNodeKind::DeclSetter:
case SDKNodeKind::DeclSubscript:
Descriptor = SDKNodeDeclAbstractFunc::
getTypeRoleDescription(Ctx, LParent->getChildIndex(LT));
break;
case SDKNodeKind::DeclVar:
Descriptor = "declared";
break;
case SDKNodeKind::DeclTypeAlias:
Descriptor = "underlying";
break;
case SDKNodeKind::DeclAssociatedType:
Descriptor = "default";
break;
}
if (LT->getPrintedName() != RT->getPrintedName()) {
Diags.diagnose(SourceLoc(), diag::decl_type_change, LParent->getScreenInfo(),
Descriptor, LT->getPrintedName(), RT->getPrintedName());
return;
}
if (LT->hasDefaultArgument() && !RT->hasDefaultArgument()) { if (LT->hasDefaultArgument() && !RT->hasDefaultArgument()) {
auto *Func = cast<SDKNodeDeclAbstractFunc>(LT->getClosestParentDecl()); Diags.diagnose(SourceLoc(), diag::default_arg_removed,
Diags.diagnose(SourceLoc(), diag::default_arg_removed, Func->getScreenInfo(), LParent->getScreenInfo(), Descriptor);
Func->getTypeRoleDescription(Ctx, Func->getChildIndex(LT))); }
if (LT->getKind() != RT->getKind())
return;
assert(LT->getKind() == RT->getKind());
switch (LT->getKind()) {
case SDKNodeKind::TypeFunc: {
auto *LFT = cast<SDKNodeTypeFunc>(LT);
auto *RFT = cast<SDKNodeTypeFunc>(RT);
if (Ctx.checkingABI() && LFT->isEscaping() != RFT->isEscaping()) {
Diags.diagnose(SourceLoc(), diag::func_type_escaping_changed,
LParent->getScreenInfo(), Descriptor, LFT->isEscaping());
}
break;
}
default:
break;
} }
} }
// This is first pass on two given SDKNode trees. This pass removes the common part // This is first pass on two given SDKNode trees. This pass removes the common part
// of two versions of SDK, leaving only the changed part. // of two versions of SDK, leaving only the changed part.
class PrunePass : public MatchedNodeListener, public SDKTreeDiffPass { class PrunePass : public MatchedNodeListener, public SDKTreeDiffPass {
@@ -1656,7 +1711,6 @@ public:
class DiagnosisEmitter : public SDKNodeVisitor { class DiagnosisEmitter : public SDKNodeVisitor {
void handle(const SDKNodeDecl *D, NodeAnnotation Anno); void handle(const SDKNodeDecl *D, NodeAnnotation Anno);
void visitType(SDKNodeType *T);
void visitDecl(SDKNodeDecl *D); void visitDecl(SDKNodeDecl *D);
void visit(NodePtr Node) override; void visit(NodePtr Node) override;
SDKNodeDecl *findAddedDecl(const SDKNodeDecl *Node); SDKNodeDecl *findAddedDecl(const SDKNodeDecl *Node);
@@ -1796,42 +1850,10 @@ void DiagnosisEmitter::visitDecl(SDKNodeDecl *Node) {
handle(Node, Anno); handle(Node, Anno);
} }
void DiagnosisEmitter::visitType(SDKNodeType *Node) {
auto *Parent = dyn_cast<SDKNodeDecl>(Node->getParent());
if (!Parent || Parent->isSDKPrivate())
return;
SDKContext &Ctx = Node->getSDKContext();
if (Node->isAnnotatedAs(NodeAnnotation::Updated)) {
auto *Count = UpdateMap.findUpdateCounterpart(Node)->getAs<SDKNodeType>();
StringRef Descriptor;
switch (Parent->getKind()) {
case SDKNodeKind::DeclConstructor:
case SDKNodeKind::DeclFunction:
case SDKNodeKind::DeclVar:
Descriptor = isa<SDKNodeDeclAbstractFunc>(Parent) ?
SDKNodeDeclAbstractFunc::getTypeRoleDescription(Ctx, Parent->getChildIndex(Node)) :
Ctx.buffer("declared");
if (Node->getPrintedName() != Count->getPrintedName())
Diags.diagnose(SourceLoc(), diag::decl_type_change, Parent->getScreenInfo(),
Descriptor, Node->getPrintedName(), Count->getPrintedName());
break;
case SDKNodeKind::DeclAssociatedType:
Diags.diagnose(SourceLoc(), diag::decl_type_change, Parent->getScreenInfo(),
"default", Node->getPrintedName(), Count->getPrintedName());
break;
default:
break;
}
}
}
void DiagnosisEmitter::visit(NodePtr Node) { void DiagnosisEmitter::visit(NodePtr Node) {
if (auto *DNode = dyn_cast<SDKNodeDecl>(Node)) { if (auto *DNode = dyn_cast<SDKNodeDecl>(Node)) {
visitDecl(DNode); visitDecl(DNode);
} }
if (auto *TNode = dyn_cast<SDKNodeType>(Node)) {
visitType(TNode);
}
} }
typedef std::vector<NoEscapeFuncParam> NoEscapeFuncParamVector; typedef std::vector<NoEscapeFuncParam> NoEscapeFuncParamVector;