From c295c6cee94cee49e6d785491788031fba6429c7 Mon Sep 17 00:00:00 2001 From: Xi Ge Date: Wed, 18 Aug 2021 15:54:56 -0700 Subject: [PATCH 1/5] ABIChecker: include import decls in the intermediate JSON file Removing an import statement can be potentially source-breaking. We should prepare for diagnosing such case. --- .../swift/APIDigester/ModuleAnalyzerNodes.h | 6 +++++ include/swift/IDE/DigesterEnums.def | 1 + lib/APIDigester/ModuleAnalyzerNodes.cpp | 20 ++++++++++++++++ test/api-digester/Outputs/apinotes-diags.txt | 1 + .../Outputs/apinotes-migrator-gen-revert.json | 11 +++++++++ .../Outputs/apinotes-migrator-gen.json | 11 +++++++++ test/api-digester/Outputs/cake-abi.json | 24 +++++++++++++++++++ test/api-digester/Outputs/cake.json | 24 +++++++++++++++++++ .../Outputs/clang-module-dump.txt | 10 ++++++++ tools/driver/swift_api_digester_main.cpp | 1 + 10 files changed, 109 insertions(+) diff --git a/include/swift/APIDigester/ModuleAnalyzerNodes.h b/include/swift/APIDigester/ModuleAnalyzerNodes.h index 91cf567f93a4c..5ba6ee1fb2117 100644 --- a/include/swift/APIDigester/ModuleAnalyzerNodes.h +++ b/include/swift/APIDigester/ModuleAnalyzerNodes.h @@ -717,6 +717,12 @@ class SDKNodeDeclAccessor: public SDKNodeDeclAbstractFunc { void jsonize(json::Output &Out) override; }; +class SDKNodeDeclImport: public SDKNodeDecl { +public: + SDKNodeDeclImport(SDKNodeInitInfo Info); + static bool classof(const SDKNode *N); +}; + // The additional information we need for a type node in the digest. // We use type node to represent entities more than types, e.g. parameters, so // this struct is necessary to pass down to create a type node. diff --git a/include/swift/IDE/DigesterEnums.def b/include/swift/IDE/DigesterEnums.def index 415db666c919d..b58b49fe0b471 100644 --- a/include/swift/IDE/DigesterEnums.def +++ b/include/swift/IDE/DigesterEnums.def @@ -72,6 +72,7 @@ NODE_KIND(DeclOperator, OperatorDecl) NODE_KIND(DeclType, TypeDecl) NODE_KIND(DeclVar, Var) NODE_KIND(DeclTypeAlias, TypeAlias) +NODE_KIND(DeclImport, Import) NODE_KIND(DeclAssociatedType, AssociatedType) NODE_KIND_RANGE(Decl, DeclFunction, DeclAssociatedType) diff --git a/lib/APIDigester/ModuleAnalyzerNodes.cpp b/lib/APIDigester/ModuleAnalyzerNodes.cpp index 88ee93e380922..fab971bca8c95 100644 --- a/lib/APIDigester/ModuleAnalyzerNodes.cpp +++ b/lib/APIDigester/ModuleAnalyzerNodes.cpp @@ -46,6 +46,7 @@ struct swift::ide::api::SDKNodeInitInfo { SDKNodeInitInfo(SDKContext &Ctx, Decl *D); SDKNodeInitInfo(SDKContext &Ctx, ValueDecl *VD); SDKNodeInitInfo(SDKContext &Ctx, OperatorDecl *D); + SDKNodeInitInfo(SDKContext &Ctx, ImportDecl *ID); SDKNodeInitInfo(SDKContext &Ctx, ProtocolConformance *Conform); SDKNodeInitInfo(SDKContext &Ctx, Type Ty, TypeInitInfo Info = TypeInitInfo()); SDKNode* createSDKNode(SDKNodeKind Kind); @@ -80,6 +81,9 @@ void SDKNodeRoot::registerDescendant(SDKNode *D) { // Operator doesn't have usr if (isa(D)) return; + // Import doesn't have usr + if (isa(D)) + return; if (auto DD = dyn_cast(D)) { assert(!DD->getUsr().empty()); DescendantDeclTable[DD->getUsr()].insert(DD); @@ -166,6 +170,9 @@ SDKNodeDeclAccessor::SDKNodeDeclAccessor(SDKNodeInitInfo Info): SDKNodeDeclAbstractFunc(Info, SDKNodeKind::DeclAccessor), AccKind(Info.AccKind) {} +SDKNodeDeclImport::SDKNodeDeclImport(SDKNodeInitInfo Info): + SDKNodeDecl(Info, SDKNodeKind::DeclImport) {} + SDKNodeDeclAssociatedType::SDKNodeDeclAssociatedType(SDKNodeInitInfo Info): SDKNodeDecl(Info, SDKNodeKind::DeclAssociatedType) {}; @@ -375,6 +382,7 @@ StringRef SDKNodeType::getTypeRoleDescription() const { case SDKNodeKind::DeclType: case SDKNodeKind::DeclOperator: case SDKNodeKind::Conformance: + case SDKNodeKind::DeclImport: llvm_unreachable("Type Parent is wrong"); case SDKNodeKind::DeclFunction: case SDKNodeKind::DeclConstructor: @@ -940,6 +948,7 @@ static bool isSDKNodeEqual(SDKContext &Ctx, const SDKNode &L, const SDKNode &R) } case SDKNodeKind::Conformance: case SDKNodeKind::TypeWitness: + case SDKNodeKind::DeclImport: case SDKNodeKind::Root: { return L.getPrintedName() == R.getPrintedName() && L.hasSameChildren(R); @@ -1358,6 +1367,13 @@ SDKNodeInitInfo::SDKNodeInitInfo(SDKContext &Ctx, OperatorDecl *OD): PrintedName = OD->getName().str(); } +SDKNodeInitInfo::SDKNodeInitInfo(SDKContext &Ctx, ImportDecl *ID): + SDKNodeInitInfo(Ctx, cast(ID)) { + std::string content; + llvm::raw_string_ostream OS(content); + ID->getModulePath().print(OS); + Name = PrintedName = Ctx.buffer(content); +} SDKNodeInitInfo::SDKNodeInitInfo(SDKContext &Ctx, ProtocolConformance *Conform): SDKNodeInitInfo(Ctx, Conform->getProtocol()) { @@ -1888,6 +1904,10 @@ void SwiftDeclCollector::processDecl(Decl *D) { if (auto *OD = dyn_cast(D)) { RootNode->addChild(constructOperatorDeclNode(OD)); } + if (auto *IM = dyn_cast(D)) { + RootNode->addChild(SDKNodeInitInfo(Ctx, IM) + .createSDKNode(SDKNodeKind::DeclImport)); + } } void SwiftDeclCollector::processValueDecl(ValueDecl *VD) { diff --git a/test/api-digester/Outputs/apinotes-diags.txt b/test/api-digester/Outputs/apinotes-diags.txt index 7fc6938e7ffe3..b9db649487959 100644 --- a/test/api-digester/Outputs/apinotes-diags.txt +++ b/test/api-digester/Outputs/apinotes-diags.txt @@ -15,6 +15,7 @@ APINotesTest(APINotesTest.h): TypeAlias CatAttributeName has been removed APINotesTest(APINotesTest.h): Protocol SwiftTypeWithMethodLeft has been renamed to Protocol SwiftTypeWithMethodRight APINotesTest(APINotesTest.h): Var OldType.oldMember has been renamed to Var NewType.newMember APINotesTest(APINotesTest.h): Var globalAttributeName has been renamed to Var AnimalAttributeName.globalAttributeName +APINotesTest: Import Foundation has been renamed to Import objc_generics /* Type Changes */ APINotesTest(APINotesTest.h): Constructor Cat.init(name:) has return type change from APINotesTest.Cat to APINotesTest.Cat? diff --git a/test/api-digester/Outputs/apinotes-migrator-gen-revert.json b/test/api-digester/Outputs/apinotes-migrator-gen-revert.json index 3709410ff5d11..ff0d898e9df12 100644 --- a/test/api-digester/Outputs/apinotes-migrator-gen-revert.json +++ b/test/api-digester/Outputs/apinotes-migrator-gen-revert.json @@ -1,4 +1,15 @@ [ + { + "DiffItemKind": "CommonDiffItem", + "NodeKind": "Import", + "NodeAnnotation": "Rename", + "ChildIndex": "0", + "LeftUsr": "", + "LeftComment": "objc_generics", + "RightUsr": "", + "RightComment": "Foundation", + "ModuleName": "APINotesTest" + }, { "DiffItemKind": "CommonDiffItem", "NodeKind": "Var", diff --git a/test/api-digester/Outputs/apinotes-migrator-gen.json b/test/api-digester/Outputs/apinotes-migrator-gen.json index b0c6eb4d837e7..1cdde964dfb86 100644 --- a/test/api-digester/Outputs/apinotes-migrator-gen.json +++ b/test/api-digester/Outputs/apinotes-migrator-gen.json @@ -1,4 +1,15 @@ [ + { + "DiffItemKind": "CommonDiffItem", + "NodeKind": "Import", + "NodeAnnotation": "Rename", + "ChildIndex": "0", + "LeftUsr": "", + "LeftComment": "Foundation", + "RightUsr": "", + "RightComment": "objc_generics", + "ModuleName": "APINotesTest" + }, { "DiffItemKind": "CommonDiffItem", "NodeKind": "Var", diff --git a/test/api-digester/Outputs/cake-abi.json b/test/api-digester/Outputs/cake-abi.json index 66c95aaf78752..46b2e407e05b2 100644 --- a/test/api-digester/Outputs/cake-abi.json +++ b/test/api-digester/Outputs/cake-abi.json @@ -3,6 +3,30 @@ "name": "TopLevel", "printedName": "TopLevel", "children": [ + { + "kind": "Import", + "name": "SwiftOnoneSupport", + "printedName": "SwiftOnoneSupport", + "declKind": "Import", + "moduleName": "cake" + }, + { + "kind": "Import", + "name": "_Concurrency", + "printedName": "_Concurrency", + "declKind": "Import", + "moduleName": "cake" + }, + { + "kind": "Import", + "name": "cake", + "printedName": "cake", + "declKind": "Import", + "moduleName": "cake", + "declAttributes": [ + "Exported" + ] + }, { "kind": "TypeDecl", "name": "P1", diff --git a/test/api-digester/Outputs/cake.json b/test/api-digester/Outputs/cake.json index 7bbedca20bd0d..e7b79d34b5f72 100644 --- a/test/api-digester/Outputs/cake.json +++ b/test/api-digester/Outputs/cake.json @@ -3,6 +3,30 @@ "name": "TopLevel", "printedName": "TopLevel", "children": [ + { + "kind": "Import", + "name": "SwiftOnoneSupport", + "printedName": "SwiftOnoneSupport", + "declKind": "Import", + "moduleName": "cake" + }, + { + "kind": "Import", + "name": "_Concurrency", + "printedName": "_Concurrency", + "declKind": "Import", + "moduleName": "cake" + }, + { + "kind": "Import", + "name": "cake", + "printedName": "cake", + "declKind": "Import", + "moduleName": "cake", + "declAttributes": [ + "Exported" + ] + }, { "kind": "TypeDecl", "name": "P1", diff --git a/test/api-digester/Outputs/clang-module-dump.txt b/test/api-digester/Outputs/clang-module-dump.txt index 7ce4dff0f1672..2e858d64ec1c3 100644 --- a/test/api-digester/Outputs/clang-module-dump.txt +++ b/test/api-digester/Outputs/clang-module-dump.txt @@ -3,6 +3,16 @@ "name": "TopLevel", "printedName": "TopLevel", "children": [ + { + "kind": "Import", + "name": "ObjectiveC", + "printedName": "ObjectiveC", + "declKind": "Import", + "moduleName": "Foo", + "declAttributes": [ + "Exported" + ] + }, { "kind": "TypeDecl", "name": "AnotherObjcProt", diff --git a/tools/driver/swift_api_digester_main.cpp b/tools/driver/swift_api_digester_main.cpp index 1e247922a1b87..93df3ad7d91fb 100644 --- a/tools/driver/swift_api_digester_main.cpp +++ b/tools/driver/swift_api_digester_main.cpp @@ -1317,6 +1317,7 @@ class PrunePass : public MatchedNodeListener, public SDKTreeDiffPass { case SDKNodeKind::DeclAccessor: case SDKNodeKind::DeclConstructor: case SDKNodeKind::DeclTypeAlias: + case SDKNodeKind::DeclImport: case SDKNodeKind::TypeFunc: case SDKNodeKind::TypeNominal: case SDKNodeKind::TypeAlias: { From 3bb780e86e78d66660da9748e5edc478a2dba353 Mon Sep 17 00:00:00 2001 From: Xi Ge Date: Thu, 19 Aug 2021 22:27:14 -0700 Subject: [PATCH 2/5] ABIChecker: minor refactoring to move code to APIDigester lib. NFC --- .../swift/APIDigester/ModuleAnalyzerNodes.h | 5 + lib/APIDigester/ModuleAnalyzerNodes.cpp | 314 +++++++++++++++++ lib/Frontend/CMakeLists.txt | 3 +- lib/Frontend/ModuleInterfaceBuilder.cpp | 1 + tools/driver/swift_api_digester_main.cpp | 319 +----------------- 5 files changed, 328 insertions(+), 314 deletions(-) diff --git a/include/swift/APIDigester/ModuleAnalyzerNodes.h b/include/swift/APIDigester/ModuleAnalyzerNodes.h index 5ba6ee1fb2117..e0e3fb682bba1 100644 --- a/include/swift/APIDigester/ModuleAnalyzerNodes.h +++ b/include/swift/APIDigester/ModuleAnalyzerNodes.h @@ -23,6 +23,7 @@ #include "clang/Sema/Lookup.h" #include "clang/Sema/Sema.h" #include "llvm/ADT/TinyPtrVector.h" +#include "llvm/ADT/STLExtras.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/FileSystem.h" @@ -787,6 +788,8 @@ class SwiftDeclCollector: public VisibleDeclConsumer { void lookupVisibleDecls(ArrayRef Modules); }; +void detectRename(SDKNode *L, SDKNode *R); + int dumpSwiftModules(const CompilerInvocation &InitInvok, const llvm::StringSet<> &ModuleNames, StringRef OutputDir, @@ -805,6 +808,8 @@ int dumpSDKContent(const CompilerInvocation &InitInvok, const llvm::StringSet<> &ModuleNames, StringRef OutputFile, CheckerOptions Opts); +void dumpModuleContent(ModuleDecl *MD, StringRef OutputFile, bool ABI); + /// Mostly for testing purposes, this function de-serializes the SDK dump in /// dumpPath and re-serialize them to OutputPath. If the tool performs correctly, /// the contents in dumpPath and OutputPath should be identical. diff --git a/lib/APIDigester/ModuleAnalyzerNodes.cpp b/lib/APIDigester/ModuleAnalyzerNodes.cpp index fab971bca8c95..7ca00d58f4dc1 100644 --- a/lib/APIDigester/ModuleAnalyzerNodes.cpp +++ b/lib/APIDigester/ModuleAnalyzerNodes.cpp @@ -2294,6 +2294,22 @@ int swift::ide::api::deserializeSDKDump(StringRef dumpPath, StringRef OutputPath return 0; } +void swift::ide::api::dumpModuleContent(ModuleDecl *MD, StringRef OutputFile, + bool ABI) { + CheckerOptions opts; + opts.ABI = ABI; + opts.SwiftOnly = true; + opts.AvoidLocation = true; + opts.AvoidToolArgs = true; + opts.Migrator = false; + opts.SkipOSCheck = false; + opts.Verbose = false; + SDKContext ctx(opts); + SwiftDeclCollector collector(ctx); + collector.lookupVisibleDecls({MD}); + dumpSDKRoot(collector.getSDKRoot(), OutputFile); +} + int swift::ide::api::findDeclUsr(StringRef dumpPath, CheckerOptions Opts) { std::error_code EC; if (!fs::exists(dumpPath)) { @@ -2323,3 +2339,301 @@ int swift::ide::api::findDeclUsr(StringRef dumpPath, CheckerOptions Opts) { } return 0; } + +void swift::ide::api::SDKNodeDeclType::diagnose(SDKNode *Right) { + SDKNodeDecl::diagnose(Right); + auto *R = dyn_cast(Right); + if (!R) + return; + auto Loc = R->getLoc(); + if (getDeclKind() != R->getDeclKind()) { + emitDiag(Loc, diag::decl_kind_changed, getDeclKindStr(R->getDeclKind(), + getSDKContext().getOpts().CompilerStyle)); + return; + } + + assert(getDeclKind() == R->getDeclKind()); + auto DKind = getDeclKind(); + switch (DKind) { + case DeclKind::Class: { + auto LSuperClass = getSuperClassName(); + auto RSuperClass = R->getSuperClassName(); + if (!LSuperClass.empty() && LSuperClass != RSuperClass) { + if (RSuperClass.empty()) { + emitDiag(Loc, diag::super_class_removed, LSuperClass); + } else if (!llvm::is_contained(R->getClassInheritanceChain(), LSuperClass)) { + emitDiag(Loc, diag::super_class_changed, LSuperClass, RSuperClass); + } + } + + // Check for @_hasMissingDesignatedInitializers and + // @_inheritsConvenienceInitializers changes. + if (isOpen() && R->isOpen()) { + // It's not safe to add new, invisible designated inits to open + // classes. + if (!hasMissingDesignatedInitializers() && + R->hasMissingDesignatedInitializers()) + R->emitDiag(R->getLoc(), diag::added_invisible_designated_init); + } + + // It's not safe to stop inheriting convenience inits, it changes + // the set of initializers that are available. + if (!Ctx.checkingABI() && + inheritsConvenienceInitializers() && + !R->inheritsConvenienceInitializers()) + R->emitDiag(R->getLoc(), diag::not_inheriting_convenience_inits); + break; + } + default: + break; + } +} + +void swift::ide::api::SDKNodeDeclAbstractFunc::diagnose(SDKNode *Right) { + SDKNodeDecl::diagnose(Right); + auto *R = dyn_cast(Right); + if (!R) + return; + auto Loc = R->getLoc(); + if (!isThrowing() && R->isThrowing()) { + emitDiag(Loc, diag::decl_new_attr, Ctx.buffer("throwing")); + } + if (Ctx.checkingABI()) { + if (reqNewWitnessTableEntry() != R->reqNewWitnessTableEntry()) { + emitDiag(Loc, diag::decl_new_witness_table_entry, reqNewWitnessTableEntry()); + } + } +} + +void swift::ide::api::SDKNodeDeclFunction::diagnose(SDKNode *Right) { + SDKNodeDeclAbstractFunc::diagnose(Right); + auto *R = dyn_cast(Right); + if (!R) + return; + auto Loc = R->getLoc(); + if (getSelfAccessKind() != R->getSelfAccessKind()) { + emitDiag(Loc, diag::func_self_access_change, getSelfAccessKind(), + R->getSelfAccessKind()); + } + if (Ctx.checkingABI()) { + if (hasFixedBinaryOrder() != R->hasFixedBinaryOrder()) { + emitDiag(Loc, diag::func_has_fixed_order_change, hasFixedBinaryOrder()); + } + } +} + +static StringRef getAttrName(DeclAttrKind Kind) { + switch (Kind) { +#define DECL_ATTR(NAME, CLASS, ...) \ + case DAK_##CLASS: \ + return DeclAttribute::isDeclModifier(DAK_##CLASS) ? #NAME : "@"#NAME; +#include "swift/AST/Attr.def" + case DAK_Count: + llvm_unreachable("unrecognized attribute kind."); + } + llvm_unreachable("covered switch"); +} + +static bool shouldDiagnoseAddingAttribute(SDKNodeDecl *D, DeclAttrKind Kind) { + return true; +} + +static bool shouldDiagnoseRemovingAttribute(SDKNodeDecl *D, DeclAttrKind Kind) { + return true; +} + +static bool isOwnershipEquivalent(ReferenceOwnership Left, + ReferenceOwnership Right) { + if (Left == Right) + return true; + if (Left == ReferenceOwnership::Unowned && Right == ReferenceOwnership::Weak) + return true; + if (Left == ReferenceOwnership::Weak && Right == ReferenceOwnership::Unowned) + return true; + return false; +} + +void swift::ide::api::detectRename(SDKNode *L, SDKNode *R) { + if (L->getKind() == R->getKind() && isa(L) && + L->getPrintedName() != R->getPrintedName()) { + L->annotate(NodeAnnotation::Rename); + L->annotate(NodeAnnotation::RenameOldName, L->getPrintedName()); + L->annotate(NodeAnnotation::RenameNewName, R->getPrintedName()); + } +} + +void swift::ide::api::SDKNodeDecl::diagnose(SDKNode *Right) { + SDKNode::diagnose(Right); + auto *RD = dyn_cast(Right); + if (!RD) + return; + detectRename(this, RD); + auto Loc = RD->getLoc(); + if (isOpen() && !RD->isOpen()) { + emitDiag(Loc, diag::no_longer_open); + } + + // Diagnose static attribute change. + if (isStatic() ^ RD->isStatic()) { + emitDiag(Loc, diag::decl_new_attr, Ctx.buffer(isStatic() ? "not static" : + "static")); + } + + // Diagnose ownership change. + if (!isOwnershipEquivalent(getReferenceOwnership(), + RD->getReferenceOwnership())) { + auto getOwnershipDescription = [&](swift::ReferenceOwnership O) { + if (O == ReferenceOwnership::Strong) + return Ctx.buffer("strong"); + return keywordOf(O); + }; + emitDiag(Loc, diag::decl_attr_change, + getOwnershipDescription(getReferenceOwnership()), + getOwnershipDescription(RD->getReferenceOwnership())); + } + // Diagnose generic signature change + if (getGenericSignature() != RD->getGenericSignature()) { + // Prefer sugared signature in diagnostics to be more user-friendly. + if (Ctx.commonVersionAtLeast(2) && + getSugaredGenericSignature() != RD->getSugaredGenericSignature()) { + emitDiag(Loc, diag::generic_sig_change, + getSugaredGenericSignature(), RD->getSugaredGenericSignature()); + } else { + emitDiag(Loc, diag::generic_sig_change, + getGenericSignature(), RD->getGenericSignature()); + } + } + + // ObjC name changes are considered breakage + if (getObjCName() != RD->getObjCName()) { + if (Ctx.commonVersionAtLeast(4)) { + emitDiag(Loc, diag::objc_name_change, getObjCName(), RD->getObjCName()); + } + } + + if (isOptional() != RD->isOptional()) { + if (Ctx.checkingABI()) { + // Both adding/removing optional is ABI-breaking. + emitDiag(Loc, diag::optional_req_changed, isOptional()); + } else if (isOptional()) { + // Removing optional is source-breaking. + emitDiag(Loc, diag::optional_req_changed, isOptional()); + } + } + + // Diagnose removing attributes. + for (auto Kind: getDeclAttributes()) { + if (!RD->hasDeclAttribute(Kind)) { + if ((Ctx.checkingABI() ? DeclAttribute::isRemovingBreakingABI(Kind) : + DeclAttribute::isRemovingBreakingAPI(Kind)) && + shouldDiagnoseRemovingAttribute(this, Kind)) { + emitDiag(Loc, diag::decl_new_attr, + Ctx.buffer((llvm::Twine("without ") + getAttrName(Kind)).str())); + } + } + } + + // Diagnose adding attributes. + for (auto Kind: RD->getDeclAttributes()) { + if (!hasDeclAttribute(Kind)) { + if ((Ctx.checkingABI() ? DeclAttribute::isAddingBreakingABI(Kind) : + DeclAttribute::isAddingBreakingAPI(Kind)) && + shouldDiagnoseAddingAttribute(this, Kind)) { + emitDiag(Loc, diag::decl_new_attr, + Ctx.buffer((llvm::Twine("with ") + getAttrName(Kind)).str())); + } + } + } + + if (Ctx.checkingABI()) { + if (hasFixedBinaryOrder() && RD->hasFixedBinaryOrder() && + getFixedBinaryOrder() != RD->getFixedBinaryOrder()) { + emitDiag(Loc, diag::decl_reorder, getFixedBinaryOrder(), + RD->getFixedBinaryOrder()); + } + } +} + +void swift::ide::api::SDKNodeDeclOperator::diagnose(SDKNode *Right) { + SDKNodeDecl::diagnose(Right); + auto *RO = dyn_cast(Right); + if (!RO) + return; + auto Loc = RO->getLoc(); + if (getDeclKind() != RO->getDeclKind()) { + emitDiag(Loc, diag::decl_kind_changed, getDeclKindStr(RO->getDeclKind(), + getSDKContext().getOpts().CompilerStyle)); + } +} + +void swift::ide::api::SDKNodeDeclVar::diagnose(SDKNode *Right) { + SDKNodeDecl::diagnose(Right); + auto *RV = dyn_cast(Right); + if (!RV) + return; + auto Loc = RV->getLoc(); + if (Ctx.checkingABI()) { + if (hasFixedBinaryOrder() != RV->hasFixedBinaryOrder()) { + emitDiag(Loc, diag::var_has_fixed_order_change, hasFixedBinaryOrder()); + } + } +} + +static bool shouldDiagnoseType(SDKNodeType *T) { + return T->isTopLevelType(); +} + +void swift::ide::api::SDKNodeType::diagnose(SDKNode *Right) { + SDKNode::diagnose(Right); + auto *RT = dyn_cast(Right); + if (!RT || !shouldDiagnoseType(this)) + return; + assert(isTopLevelType()); + + // Diagnose type witness changes when diagnosing ABI breakages. + if (auto *Wit = dyn_cast(getParent())) { + auto *Conform = Wit->getParent()->getAs(); + if (Ctx.checkingABI() && getPrintedName() != RT->getPrintedName()) { + auto *LD = Conform->getNominalTypeDecl(); + LD->emitDiag(SourceLoc(), diag::type_witness_change, + Wit->getWitnessedTypeName(), + getPrintedName(), RT->getPrintedName()); + } + return; + } + + StringRef Descriptor = getTypeRoleDescription(); + assert(isa(getParent())); + auto LParent = cast(getParent()); + assert(LParent->getKind() == RT->getParent()->getAs()->getKind()); + auto Loc = RT->getParent()->getAs()->getLoc(); + if (getPrintedName() != RT->getPrintedName()) { + LParent->emitDiag(Loc, diag::decl_type_change, + Descriptor, getPrintedName(), RT->getPrintedName()); + } + + if (hasDefaultArgument() && !RT->hasDefaultArgument()) { + LParent->emitDiag(Loc, diag::default_arg_removed, Descriptor); + } + if (getParamValueOwnership() != RT->getParamValueOwnership()) { + LParent->emitDiag(Loc, diag::param_ownership_change, + getTypeRoleDescription(), + getParamValueOwnership(), + RT->getParamValueOwnership()); + } +} + +void swift::ide::api::SDKNodeTypeFunc::diagnose(SDKNode *Right) { + SDKNodeType::diagnose(Right); + auto *RT = dyn_cast(Right); + if (!RT || !shouldDiagnoseType(this)) + return; + assert(isTopLevelType()); + auto Loc = RT->getParent()->getAs()->getLoc(); + if (Ctx.checkingABI() && isEscaping() != RT->isEscaping()) { + getParent()->getAs()->emitDiag(Loc, + diag::func_type_escaping_changed, + getTypeRoleDescription(), + isEscaping()); + } +} diff --git a/lib/Frontend/CMakeLists.txt b/lib/Frontend/CMakeLists.txt index 2c8c24f9e4dd9..c5ed0608d248d 100644 --- a/lib/Frontend/CMakeLists.txt +++ b/lib/Frontend/CMakeLists.txt @@ -27,5 +27,6 @@ target_link_libraries(swiftFrontend PRIVATE swiftLocalization swiftSema swiftSerialization - swiftTBDGen) + swiftTBDGen + swiftAPIDigester) diff --git a/lib/Frontend/ModuleInterfaceBuilder.cpp b/lib/Frontend/ModuleInterfaceBuilder.cpp index 34339aab3f649..45a65248de0e2 100644 --- a/lib/Frontend/ModuleInterfaceBuilder.cpp +++ b/lib/Frontend/ModuleInterfaceBuilder.cpp @@ -24,6 +24,7 @@ #include "swift/Frontend/ModuleInterfaceSupport.h" #include "swift/SILOptimizer/PassManager/Passes.h" #include "swift/Serialization/SerializationOptions.h" +#include "swift/APIDigester/ModuleAnalyzerNodes.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Lex/PreprocessorOptions.h" #include "llvm/ADT/Hashing.h" diff --git a/tools/driver/swift_api_digester_main.cpp b/tools/driver/swift_api_digester_main.cpp index 93df3ad7d91fb..07ebc0244d06c 100644 --- a/tools/driver/swift_api_digester_main.cpp +++ b/tools/driver/swift_api_digester_main.cpp @@ -292,16 +292,6 @@ struct MatchedNodeListener { virtual ~MatchedNodeListener() = default; }; -template -bool contains(std::vector &container, T *instance) { - return std::find(container.begin(), container.end(), instance) != container.end(); -} - -template -bool contains(ArrayRef container, T instance) { - return std::find(container.begin(), container.end(), instance) != container.end(); -} - static void singleMatch(SDKNode* Left, SDKNode *Right, MatchedNodeListener &Listener) { @@ -400,7 +390,7 @@ class RemovedAddedNodeMatcher : public NodeMatcher, public MatchedNodeListener { void handleUnmatch(NodeVector &Matched, NodeVector &All, bool Left) { for (auto A : All) { - if (contains(Matched, A)) + if (llvm::is_contained(Matched, A)) continue; if (Left) Listener.foundMatch(A, nullptr, NodeMatchReason::Removed); @@ -597,12 +587,12 @@ class RemovedAddedNodeMatcher : public NodeMatcher, public MatchedNodeListener { NodeVector RenameRight; for (auto Remain : Removed) { - if (!contains(RemovedMatched, Remain)) + if (!llvm::is_contained(RemovedMatched, Remain)) RenameLeft.push_back(Remain); } for (auto Remain : Added) { - if (!contains(AddedMatched, Remain)) + if (!llvm::is_contained(AddedMatched, Remain)) RenameRight.push_back(Remain); } @@ -704,7 +694,7 @@ void SameNameNodeMatcher::match() { for (auto *RN : Right) { // If RN has matched before, ignore it. - if (contains(MatchedRight, RN)) + if (llvm::is_contained(MatchedRight, RN)) continue; // If LN and RN have the same name for some reason, keep track of RN. @@ -723,7 +713,7 @@ void SameNameNodeMatcher::match() { } } for (auto &R : Right) { - if (!contains(MatchedRight, R)) { + if (!llvm::is_contained(MatchedRight, R)) { Added.push_back(R); } } @@ -766,305 +756,8 @@ class SDKTreeDiffPass { virtual void pass(NodePtr Left, NodePtr Right) = 0; virtual ~SDKTreeDiffPass() {} }; - -static void detectRename(SDKNode *L, SDKNode *R) { - if (L->getKind() == R->getKind() && isa(L) && - L->getPrintedName() != R->getPrintedName()) { - L->annotate(NodeAnnotation::Rename); - L->annotate(NodeAnnotation::RenameOldName, L->getPrintedName()); - L->annotate(NodeAnnotation::RenameNewName, R->getPrintedName()); - } -} - -static bool isOwnershipEquivalent(ReferenceOwnership Left, - ReferenceOwnership Right) { - if (Left == Right) - return true; - if (Left == ReferenceOwnership::Unowned && Right == ReferenceOwnership::Weak) - return true; - if (Left == ReferenceOwnership::Weak && Right == ReferenceOwnership::Unowned) - return true; - return false; -} }// End of anonymous namespace -void swift::ide::api::SDKNodeDeclType::diagnose(SDKNode *Right) { - SDKNodeDecl::diagnose(Right); - auto *R = dyn_cast(Right); - if (!R) - return; - auto Loc = R->getLoc(); - if (getDeclKind() != R->getDeclKind()) { - emitDiag(Loc, diag::decl_kind_changed, getDeclKindStr(R->getDeclKind(), - getSDKContext().getOpts().CompilerStyle)); - return; - } - - assert(getDeclKind() == R->getDeclKind()); - auto DKind = getDeclKind(); - switch (DKind) { - case DeclKind::Class: { - auto LSuperClass = getSuperClassName(); - auto RSuperClass = R->getSuperClassName(); - if (!LSuperClass.empty() && LSuperClass != RSuperClass) { - if (RSuperClass.empty()) { - emitDiag(Loc, diag::super_class_removed, LSuperClass); - } else if (!contains(R->getClassInheritanceChain(), LSuperClass)) { - emitDiag(Loc, diag::super_class_changed, LSuperClass, RSuperClass); - } - } - - // Check for @_hasMissingDesignatedInitializers and - // @_inheritsConvenienceInitializers changes. - if (isOpen() && R->isOpen()) { - // It's not safe to add new, invisible designated inits to open - // classes. - if (!hasMissingDesignatedInitializers() && - R->hasMissingDesignatedInitializers()) - R->emitDiag(R->getLoc(), diag::added_invisible_designated_init); - } - - // It's not safe to stop inheriting convenience inits, it changes - // the set of initializers that are available. - if (inheritsConvenienceInitializers() && - !R->inheritsConvenienceInitializers()) - R->emitDiag(R->getLoc(), diag::not_inheriting_convenience_inits); - break; - } - default: - break; - } -} - -void swift::ide::api::SDKNodeDeclAbstractFunc::diagnose(SDKNode *Right) { - SDKNodeDecl::diagnose(Right); - auto *R = dyn_cast(Right); - if (!R) - return; - auto Loc = R->getLoc(); - if (!isThrowing() && R->isThrowing()) { - emitDiag(Loc, diag::decl_new_attr, Ctx.buffer("throwing")); - } - if (Ctx.checkingABI()) { - if (reqNewWitnessTableEntry() != R->reqNewWitnessTableEntry()) { - emitDiag(Loc, diag::decl_new_witness_table_entry, reqNewWitnessTableEntry()); - } - } -} - -void swift::ide::api::SDKNodeDeclFunction::diagnose(SDKNode *Right) { - SDKNodeDeclAbstractFunc::diagnose(Right); - auto *R = dyn_cast(Right); - if (!R) - return; - auto Loc = R->getLoc(); - if (getSelfAccessKind() != R->getSelfAccessKind()) { - emitDiag(Loc, diag::func_self_access_change, getSelfAccessKind(), - R->getSelfAccessKind()); - } - if (Ctx.checkingABI()) { - if (hasFixedBinaryOrder() != R->hasFixedBinaryOrder()) { - emitDiag(Loc, diag::func_has_fixed_order_change, hasFixedBinaryOrder()); - } - } -} - -static StringRef getAttrName(DeclAttrKind Kind) { - switch (Kind) { -#define DECL_ATTR(NAME, CLASS, ...) \ - case DAK_##CLASS: \ - return DeclAttribute::isDeclModifier(DAK_##CLASS) ? #NAME : "@"#NAME; -#include "swift/AST/Attr.def" - case DAK_Count: - llvm_unreachable("unrecognized attribute kind."); - } - llvm_unreachable("covered switch"); -} - -static bool shouldDiagnoseAddingAttribute(SDKNodeDecl *D, DeclAttrKind Kind) { - return true; -} - -static bool shouldDiagnoseRemovingAttribute(SDKNodeDecl *D, DeclAttrKind Kind) { - return true; -} - -void swift::ide::api::SDKNodeDecl::diagnose(SDKNode *Right) { - SDKNode::diagnose(Right); - auto *RD = dyn_cast(Right); - if (!RD) - return; - detectRename(this, RD); - auto Loc = RD->getLoc(); - if (isOpen() && !RD->isOpen()) { - emitDiag(Loc, diag::no_longer_open); - } - - // Diagnose static attribute change. - if (isStatic() ^ RD->isStatic()) { - emitDiag(Loc, diag::decl_new_attr, Ctx.buffer(isStatic() ? "not static" : - "static")); - } - - // Diagnose ownership change. - if (!isOwnershipEquivalent(getReferenceOwnership(), - RD->getReferenceOwnership())) { - auto getOwnershipDescription = [&](swift::ReferenceOwnership O) { - if (O == ReferenceOwnership::Strong) - return Ctx.buffer("strong"); - return keywordOf(O); - }; - emitDiag(Loc, diag::decl_attr_change, - getOwnershipDescription(getReferenceOwnership()), - getOwnershipDescription(RD->getReferenceOwnership())); - } - // Diagnose generic signature change - if (getGenericSignature() != RD->getGenericSignature()) { - // Prefer sugared signature in diagnostics to be more user-friendly. - if (Ctx.commonVersionAtLeast(2) && - getSugaredGenericSignature() != RD->getSugaredGenericSignature()) { - emitDiag(Loc, diag::generic_sig_change, - getSugaredGenericSignature(), RD->getSugaredGenericSignature()); - } else { - emitDiag(Loc, diag::generic_sig_change, - getGenericSignature(), RD->getGenericSignature()); - } - } - - // ObjC name changes are considered breakage - if (getObjCName() != RD->getObjCName()) { - if (Ctx.commonVersionAtLeast(4)) { - emitDiag(Loc, diag::objc_name_change, getObjCName(), RD->getObjCName()); - } - } - - if (isOptional() != RD->isOptional()) { - if (Ctx.checkingABI()) { - // Both adding/removing optional is ABI-breaking. - emitDiag(Loc, diag::optional_req_changed, isOptional()); - } else if (isOptional()) { - // Removing optional is source-breaking. - emitDiag(Loc, diag::optional_req_changed, isOptional()); - } - } - - // Diagnose removing attributes. - for (auto Kind: getDeclAttributes()) { - if (!RD->hasDeclAttribute(Kind)) { - if ((Ctx.checkingABI() ? DeclAttribute::isRemovingBreakingABI(Kind) : - DeclAttribute::isRemovingBreakingAPI(Kind)) && - shouldDiagnoseRemovingAttribute(this, Kind)) { - emitDiag(Loc, diag::decl_new_attr, - Ctx.buffer((llvm::Twine("without ") + getAttrName(Kind)).str())); - } - } - } - - // Diagnose adding attributes. - for (auto Kind: RD->getDeclAttributes()) { - if (!hasDeclAttribute(Kind)) { - if ((Ctx.checkingABI() ? DeclAttribute::isAddingBreakingABI(Kind) : - DeclAttribute::isAddingBreakingAPI(Kind)) && - shouldDiagnoseAddingAttribute(this, Kind)) { - emitDiag(Loc, diag::decl_new_attr, - Ctx.buffer((llvm::Twine("with ") + getAttrName(Kind)).str())); - } - } - } - - if (Ctx.checkingABI()) { - if (hasFixedBinaryOrder() && RD->hasFixedBinaryOrder() && - getFixedBinaryOrder() != RD->getFixedBinaryOrder()) { - emitDiag(Loc, diag::decl_reorder, getFixedBinaryOrder(), - RD->getFixedBinaryOrder()); - } - } -} - -void swift::ide::api::SDKNodeDeclOperator::diagnose(SDKNode *Right) { - SDKNodeDecl::diagnose(Right); - auto *RO = dyn_cast(Right); - if (!RO) - return; - auto Loc = RO->getLoc(); - if (getDeclKind() != RO->getDeclKind()) { - emitDiag(Loc, diag::decl_kind_changed, getDeclKindStr(RO->getDeclKind(), - getSDKContext().getOpts().CompilerStyle)); - } -} - -void swift::ide::api::SDKNodeDeclVar::diagnose(SDKNode *Right) { - SDKNodeDecl::diagnose(Right); - auto *RV = dyn_cast(Right); - if (!RV) - return; - auto Loc = RV->getLoc(); - if (Ctx.checkingABI()) { - if (hasFixedBinaryOrder() != RV->hasFixedBinaryOrder()) { - emitDiag(Loc, diag::var_has_fixed_order_change, hasFixedBinaryOrder()); - } - } -} - -static bool shouldDiagnoseType(SDKNodeType *T) { - return T->isTopLevelType(); -} - -void swift::ide::api::SDKNodeType::diagnose(SDKNode *Right) { - SDKNode::diagnose(Right); - auto *RT = dyn_cast(Right); - if (!RT || !shouldDiagnoseType(this)) - return; - assert(isTopLevelType()); - - // Diagnose type witness changes when diagnosing ABI breakages. - if (auto *Wit = dyn_cast(getParent())) { - auto *Conform = Wit->getParent()->getAs(); - if (Ctx.checkingABI() && getPrintedName() != RT->getPrintedName()) { - auto *LD = Conform->getNominalTypeDecl(); - LD->emitDiag(SourceLoc(), diag::type_witness_change, - Wit->getWitnessedTypeName(), - getPrintedName(), RT->getPrintedName()); - } - return; - } - - StringRef Descriptor = getTypeRoleDescription(); - assert(isa(getParent())); - auto LParent = cast(getParent()); - assert(LParent->getKind() == RT->getParent()->getAs()->getKind()); - auto Loc = RT->getParent()->getAs()->getLoc(); - if (getPrintedName() != RT->getPrintedName()) { - LParent->emitDiag(Loc, diag::decl_type_change, - Descriptor, getPrintedName(), RT->getPrintedName()); - } - - if (hasDefaultArgument() && !RT->hasDefaultArgument()) { - LParent->emitDiag(Loc, diag::default_arg_removed, Descriptor); - } - if (getParamValueOwnership() != RT->getParamValueOwnership()) { - LParent->emitDiag(Loc, diag::param_ownership_change, - getTypeRoleDescription(), - getParamValueOwnership(), - RT->getParamValueOwnership()); - } -} - -void swift::ide::api::SDKNodeTypeFunc::diagnose(SDKNode *Right) { - SDKNodeType::diagnose(Right); - auto *RT = dyn_cast(Right); - if (!RT || !shouldDiagnoseType(this)) - return; - assert(isTopLevelType()); - auto Loc = RT->getParent()->getAs()->getLoc(); - if (Ctx.checkingABI() && isEscaping() != RT->isEscaping()) { - getParent()->getAs()->emitDiag(Loc, - diag::func_type_escaping_changed, - getTypeRoleDescription(), - isEscaping()); - } -} - namespace { static void diagnoseRemovedDecl(const SDKNodeDecl *D) { if (D->getSDKContext().checkingABI()) { @@ -2258,7 +1951,7 @@ class RenameDetectorForMemberDiff : public MatchedNodeListener { void foundMatch(NodePtr Left, NodePtr Right, NodeMatchReason Reason) override { if (!Left || !Right) return; - detectRename(Left, Right); + swift::ide::api::detectRename(Left, Right); LeftDetector.detect(Left, Right); RightDetector.detect(Right, Left); } From 5415541a0adc8a5ef0635f9ecc290c5313eb9fa5 Mon Sep 17 00:00:00 2001 From: Xi Ge Date: Fri, 20 Aug 2021 13:41:33 -0700 Subject: [PATCH 3/5] Frontend: teach -compile-module-from-interface action to emit ABI descriptor as byproduct We have implemented a libSwiftDriver-based tool to generate prebuilt module cache for entire SDKs. Anchored on the same infrastructure, we could also generate ABI baselines for entire SDKs. --- include/swift/AST/DiagnosticsFrontend.def | 2 + .../swift/Basic/SupplementaryOutputPaths.h | 7 +++- .../swift/Frontend/FrontendInputsAndOutputs.h | 1 + include/swift/Frontend/FrontendOptions.h | 1 + .../swift/Frontend/ModuleInterfaceLoader.h | 2 +- include/swift/Option/FrontendOptions.td | 4 ++ .../ArgsToFrontendOptionsConverter.cpp | 5 +++ .../ArgsToFrontendOutputsConverter.cpp | 9 +++- lib/Frontend/FrontendInputsAndOutputs.cpp | 6 +++ lib/Frontend/FrontendOptions.cpp | 42 ++++++++++++++++++- lib/Frontend/ModuleInterfaceBuilder.cpp | 3 ++ lib/Frontend/ModuleInterfaceBuilder.h | 3 ++ lib/Frontend/ModuleInterfaceLoader.cpp | 6 +-- lib/FrontendTool/FrontendTool.cpp | 4 +- .../ModuleInterface/emit-abi-descriptor.swift | 13 ++++++ 15 files changed, 100 insertions(+), 8 deletions(-) create mode 100644 test/ModuleInterface/emit-abi-descriptor.swift diff --git a/include/swift/AST/DiagnosticsFrontend.def b/include/swift/AST/DiagnosticsFrontend.def index 11c20cb8e329d..7c7a4bb4c4454 100644 --- a/include/swift/AST/DiagnosticsFrontend.def +++ b/include/swift/AST/DiagnosticsFrontend.def @@ -129,6 +129,8 @@ ERROR(error_mode_cannot_emit_module_summary,none, "this mode does not support emitting module summary files", ()) ERROR(error_mode_cannot_emit_symbol_graph,none, "this mode does not support emitting symbol graph files", ()) +ERROR(error_mode_cannot_emit_abi_descriptor,none, + "this mode does not support emitting ABI descriptor", ()) ERROR(cannot_emit_ir_skipping_function_bodies,none, "the -experimental-skip-*-function-bodies* flags do not support " "emitting IR", ()) diff --git a/include/swift/Basic/SupplementaryOutputPaths.h b/include/swift/Basic/SupplementaryOutputPaths.h index e72f0ecb02b10..54aabef9ffc9f 100644 --- a/include/swift/Basic/SupplementaryOutputPaths.h +++ b/include/swift/Basic/SupplementaryOutputPaths.h @@ -153,6 +153,9 @@ struct SupplementaryOutputPaths { /// The path to which we should emit module summary file. std::string ModuleSummaryOutputPath; + /// The output path to generate ABI baseline. + std::string ABIDescriptorOutputPath; + SupplementaryOutputPaths() = default; SupplementaryOutputPaths(const SupplementaryOutputPaths &) = default; @@ -186,6 +189,8 @@ struct SupplementaryOutputPaths { fn(LdAddCFilePath); if (!ModuleSummaryOutputPath.empty()) fn(ModuleSummaryOutputPath); + if (!ABIDescriptorOutputPath.empty()) + fn(ABIDescriptorOutputPath); } bool empty() const { @@ -194,7 +199,7 @@ struct SupplementaryOutputPaths { ReferenceDependenciesFilePath.empty() && SerializedDiagnosticsPath.empty() && LoadedModuleTracePath.empty() && TBDPath.empty() && ModuleInterfaceOutputPath.empty() && - ModuleSourceInfoOutputPath.empty() && LdAddCFilePath.empty(); + ModuleSourceInfoOutputPath.empty() && LdAddCFilePath.empty() && ABIDescriptorOutputPath.empty(); } }; } // namespace swift diff --git a/include/swift/Frontend/FrontendInputsAndOutputs.h b/include/swift/Frontend/FrontendInputsAndOutputs.h index 28f60519074b4..4ccb1fc14860d 100644 --- a/include/swift/Frontend/FrontendInputsAndOutputs.h +++ b/include/swift/Frontend/FrontendInputsAndOutputs.h @@ -256,6 +256,7 @@ class FrontendInputsAndOutputs { bool hasModuleSourceInfoOutputPath() const; bool hasModuleInterfaceOutputPath() const; bool hasPrivateModuleInterfaceOutputPath() const; + bool hasABIDescriptorOutputPath() const; bool hasModuleSummaryOutputPath() const; bool hasTBDPath() const; diff --git a/include/swift/Frontend/FrontendOptions.h b/include/swift/Frontend/FrontendOptions.h index 439d8dcbba538..8406058b87221 100644 --- a/include/swift/Frontend/FrontendOptions.h +++ b/include/swift/Frontend/FrontendOptions.h @@ -423,6 +423,7 @@ class FrontendOptions { static bool canActionEmitModuleDoc(ActionType); static bool canActionEmitModuleSummary(ActionType); static bool canActionEmitInterface(ActionType); + static bool canActionEmitABIDescriptor(ActionType); public: static bool doesActionGenerateSIL(ActionType); diff --git a/include/swift/Frontend/ModuleInterfaceLoader.h b/include/swift/Frontend/ModuleInterfaceLoader.h index 90469b2372b2e..f947c60859b38 100644 --- a/include/swift/Frontend/ModuleInterfaceLoader.h +++ b/include/swift/Frontend/ModuleInterfaceLoader.h @@ -414,7 +414,7 @@ class ModuleInterfaceLoader : public SerializedModuleLoaderBase { const SearchPathOptions &SearchPathOpts, const LangOptions &LangOpts, const ClangImporterOptions &ClangOpts, StringRef CacheDir, StringRef PrebuiltCacheDir, StringRef ModuleName, StringRef InPath, - StringRef OutPath, bool SerializeDependencyHashes, + StringRef OutPath, StringRef ABIOutputPath, bool SerializeDependencyHashes, bool TrackSystemDependencies, ModuleInterfaceLoaderOptions Opts, RequireOSSAModules_t RequireOSSAModules); }; diff --git a/include/swift/Option/FrontendOptions.td b/include/swift/Option/FrontendOptions.td index 3c1bd619c9be5..5252ae38f5be4 100644 --- a/include/swift/Option/FrontendOptions.td +++ b/include/swift/Option/FrontendOptions.td @@ -64,6 +64,10 @@ def emit_fixits_path : Separate<["-"], "emit-fixits-path">, MetaVarName<"">, HelpText<"Output compiler fixits as source edits to ">; +def emit_abi_descriptor_path + : Separate<["-"], "emit-abi-descriptor-path">, MetaVarName<"">, + HelpText<"Output the ABI descriptor of current module to ">; + def serialize_module_interface_dependency_hashes : Flag<["-"], "serialize-module-interface-dependency-hashes">, Flags<[HelpHidden]>; diff --git a/lib/Frontend/ArgsToFrontendOptionsConverter.cpp b/lib/Frontend/ArgsToFrontendOptionsConverter.cpp index adf4df00b0e6f..bf9b030b9a473 100644 --- a/lib/Frontend/ArgsToFrontendOptionsConverter.cpp +++ b/lib/Frontend/ArgsToFrontendOptionsConverter.cpp @@ -594,6 +594,11 @@ bool ArgsToFrontendOptionsConverter::checkUnusedSupplementaryOutputPaths() Diags.diagnose(SourceLoc(), diag::error_mode_cannot_emit_module_doc); return true; } + if (!FrontendOptions::canActionEmitABIDescriptor(Opts.RequestedAction) && + Opts.InputsAndOutputs.hasABIDescriptorOutputPath()) { + Diags.diagnose(SourceLoc(), diag::error_mode_cannot_emit_abi_descriptor); + return true; + } // If we cannot emit module doc, we cannot emit source information file either. if (!FrontendOptions::canActionEmitModuleDoc(Opts.RequestedAction) && Opts.InputsAndOutputs.hasModuleSourceInfoOutputPath()) { diff --git a/lib/Frontend/ArgsToFrontendOutputsConverter.cpp b/lib/Frontend/ArgsToFrontendOutputsConverter.cpp index 0c042e7a12d05..0d95a416d0120 100644 --- a/lib/Frontend/ArgsToFrontendOutputsConverter.cpp +++ b/lib/Frontend/ArgsToFrontendOutputsConverter.cpp @@ -341,11 +341,14 @@ SupplementaryOutputPathsComputer::getSupplementaryOutputPathsFromArguments() options::OPT_emit_ldadd_cfile_path); auto moduleSummaryOutput = getSupplementaryFilenamesFromArguments( options::OPT_emit_module_summary_path); + auto abiDescriptorOutput = getSupplementaryFilenamesFromArguments( + options::OPT_emit_abi_descriptor_path); if (!objCHeaderOutput || !moduleOutput || !moduleDocOutput || !dependenciesFile || !referenceDependenciesFile || !serializedDiagnostics || !fixItsOutput || !loadedModuleTrace || !TBD || !moduleInterfaceOutput || !privateModuleInterfaceOutput || - !moduleSourceInfoOutput || !ldAddCFileOutput || !moduleSummaryOutput) { + !moduleSourceInfoOutput || !ldAddCFileOutput || !moduleSummaryOutput || + !abiDescriptorOutput) { return None; } std::vector result; @@ -368,6 +371,7 @@ SupplementaryOutputPathsComputer::getSupplementaryOutputPathsFromArguments() sop.ModuleSourceInfoOutputPath = (*moduleSourceInfoOutput)[i]; sop.LdAddCFilePath = (*ldAddCFileOutput)[i]; sop.ModuleSummaryOutputPath = (*moduleSummaryOutput)[i]; + sop.ABIDescriptorOutputPath = (*abiDescriptorOutput)[i]; result.push_back(sop); } return result; @@ -466,6 +470,8 @@ SupplementaryOutputPathsComputer::computeOutputPathsForOneInput( auto PrivateModuleInterfaceOutputPath = pathsFromArguments.PrivateModuleInterfaceOutputPath; + // There is no non-path form of -emit-abi-descriptor-path + auto ABIDescriptorOutputPath = pathsFromArguments.ABIDescriptorOutputPath; ID emitModuleOption; std::string moduleExtension; std::string mainOutputIfUsableForModule; @@ -492,6 +498,7 @@ SupplementaryOutputPathsComputer::computeOutputPathsForOneInput( sop.ModuleSourceInfoOutputPath = moduleSourceInfoOutputPath; sop.LdAddCFilePath = pathsFromArguments.LdAddCFilePath; sop.ModuleSummaryOutputPath = moduleSummaryOutputPath; + sop.ABIDescriptorOutputPath = ABIDescriptorOutputPath; return sop; } diff --git a/lib/Frontend/FrontendInputsAndOutputs.cpp b/lib/Frontend/FrontendInputsAndOutputs.cpp index c65b43e6543be..d76a2db1b0a6d 100644 --- a/lib/Frontend/FrontendInputsAndOutputs.cpp +++ b/lib/Frontend/FrontendInputsAndOutputs.cpp @@ -503,6 +503,12 @@ bool FrontendInputsAndOutputs::hasPrivateModuleInterfaceOutputPath() const { return outs.PrivateModuleInterfaceOutputPath; }); } +bool FrontendInputsAndOutputs::hasABIDescriptorOutputPath() const { + return hasSupplementaryOutputPath( + [](const SupplementaryOutputPaths &outs) -> const std::string & { + return outs.ABIDescriptorOutputPath; + }); +} bool FrontendInputsAndOutputs::hasModuleSummaryOutputPath() const { return hasSupplementaryOutputPath( [](const SupplementaryOutputPaths &outs) -> const std::string & { diff --git a/lib/Frontend/FrontendOptions.cpp b/lib/Frontend/FrontendOptions.cpp index 452c1e5b99f87..08e0d14243cf1 100644 --- a/lib/Frontend/FrontendOptions.cpp +++ b/lib/Frontend/FrontendOptions.cpp @@ -524,7 +524,47 @@ bool FrontendOptions::canActionEmitLoadedModuleTrace(ActionType action) { } llvm_unreachable("unhandled action"); } - +bool FrontendOptions::canActionEmitABIDescriptor(ActionType action) { + switch (action) { + case ActionType::CompileModuleFromInterface: + return true; + case ActionType::NoneAction: + case ActionType::Parse: + case ActionType::ResolveImports: + case ActionType::Typecheck: + case ActionType::DumpParse: + case ActionType::DumpInterfaceHash: + case ActionType::DumpAST: + case ActionType::EmitSyntax: + case ActionType::PrintAST: + case ActionType::EmitPCH: + case ActionType::DumpScopeMaps: + case ActionType::DumpTypeRefinementContexts: + case ActionType::DumpTypeInfo: + case ActionType::EmitSILGen: + case ActionType::TypecheckModuleFromInterface: + case ActionType::Immediate: + case ActionType::REPL: + case ActionType::EmitPCM: + case ActionType::DumpPCM: + case ActionType::ScanDependencies: + case ActionType::PrintVersion: + case ActionType::PrintFeature: + case ActionType::MergeModules: + case ActionType::EmitModuleOnly: + case ActionType::EmitSIL: + case ActionType::EmitSIBGen: + case ActionType::EmitSIB: + case ActionType::EmitIRGen: + case ActionType::EmitIR: + case ActionType::EmitBC: + case ActionType::EmitAssembly: + case ActionType::EmitObject: + case ActionType::EmitImportedModules: + return false; + } + llvm_unreachable("unhandled action"); +} bool FrontendOptions::canActionEmitModule(ActionType action) { switch (action) { case ActionType::NoneAction: diff --git a/lib/Frontend/ModuleInterfaceBuilder.cpp b/lib/Frontend/ModuleInterfaceBuilder.cpp index 45a65248de0e2..50b5c02b5eb30 100644 --- a/lib/Frontend/ModuleInterfaceBuilder.cpp +++ b/lib/Frontend/ModuleInterfaceBuilder.cpp @@ -279,6 +279,9 @@ bool ModuleInterfaceBuilder::buildSwiftModuleInternal( if (SubInstance.getDiags().hadAnyError()) { return std::make_error_code(std::errc::not_supported); } + if (!ABIDescriptorPath.empty()) { + swift::ide::api::dumpModuleContent(Mod, ABIDescriptorPath, true); + } return std::error_code(); }); }, ThreadStackSize); diff --git a/lib/Frontend/ModuleInterfaceBuilder.h b/lib/Frontend/ModuleInterfaceBuilder.h index ae90b2ded60f9..ea73c5d235d3e 100644 --- a/lib/Frontend/ModuleInterfaceBuilder.h +++ b/lib/Frontend/ModuleInterfaceBuilder.h @@ -41,6 +41,7 @@ class ModuleInterfaceBuilder { const StringRef moduleName; const StringRef moduleCachePath; const StringRef prebuiltCachePath; + const StringRef ABIDescriptorPath; const bool disableInterfaceFileLock; const SourceLoc diagnosticLoc; DependencyTracker *const dependencyTracker; @@ -94,6 +95,7 @@ class ModuleInterfaceBuilder { StringRef moduleName, StringRef moduleCachePath, StringRef prebuiltCachePath, + StringRef ABIDescriptorPath, bool disableInterfaceFileLock = false, SourceLoc diagnosticLoc = SourceLoc(), DependencyTracker *tracker = nullptr) @@ -101,6 +103,7 @@ class ModuleInterfaceBuilder { subASTDelegate(subASTDelegate), interfacePath(interfacePath), moduleName(moduleName), moduleCachePath(moduleCachePath), prebuiltCachePath(prebuiltCachePath), + ABIDescriptorPath(ABIDescriptorPath), disableInterfaceFileLock(disableInterfaceFileLock), diagnosticLoc(diagnosticLoc), dependencyTracker(tracker) {} diff --git a/lib/Frontend/ModuleInterfaceLoader.cpp b/lib/Frontend/ModuleInterfaceLoader.cpp index 83d4517fe8850..e9770d704364b 100644 --- a/lib/Frontend/ModuleInterfaceLoader.cpp +++ b/lib/Frontend/ModuleInterfaceLoader.cpp @@ -918,7 +918,7 @@ class ModuleInterfaceLoaderImpl { // the genericSubInvocation we'll need to use to compute the cache paths. ModuleInterfaceBuilder builder( ctx.SourceMgr, ctx.Diags, astDelegate, interfacePath, moduleName, cacheDir, - prebuiltCacheDir, + prebuiltCacheDir, StringRef(), Opts.disableInterfaceLock, diagnosticLoc, dependencyTracker); @@ -1093,7 +1093,7 @@ bool ModuleInterfaceLoader::buildSwiftModuleFromSwiftInterface( const SearchPathOptions &SearchPathOpts, const LangOptions &LangOpts, const ClangImporterOptions &ClangOpts, StringRef CacheDir, StringRef PrebuiltCacheDir, StringRef ModuleName, StringRef InPath, - StringRef OutPath, bool SerializeDependencyHashes, + StringRef OutPath, StringRef ABIOutputPath, bool SerializeDependencyHashes, bool TrackSystemDependencies, ModuleInterfaceLoaderOptions LoaderOpts, RequireOSSAModules_t RequireOSSAModules) { InterfaceSubContextDelegateImpl astDelegate( @@ -1101,7 +1101,7 @@ bool ModuleInterfaceLoader::buildSwiftModuleFromSwiftInterface( /*CreateCacheDirIfAbsent*/ true, CacheDir, PrebuiltCacheDir, SerializeDependencyHashes, TrackSystemDependencies, RequireOSSAModules); ModuleInterfaceBuilder builder(SourceMgr, Diags, astDelegate, InPath, - ModuleName, CacheDir, PrebuiltCacheDir, + ModuleName, CacheDir, PrebuiltCacheDir, ABIOutputPath, LoaderOpts.disableInterfaceLock); // FIXME: We really only want to serialize 'important' dependencies here, if // we want to ship the built swiftmodules to another machine. diff --git a/lib/FrontendTool/FrontendTool.cpp b/lib/FrontendTool/FrontendTool.cpp index a68c87cd55295..b01c4c85a5cff 100644 --- a/lib/FrontendTool/FrontendTool.cpp +++ b/lib/FrontendTool/FrontendTool.cpp @@ -412,12 +412,14 @@ static bool buildModuleFromInterface(CompilerInstance &Instance) { StringRef InputPath = FEOpts.InputsAndOutputs.getFilenameOfFirstInput(); StringRef PrebuiltCachePath = FEOpts.PrebuiltModuleCachePath; ModuleInterfaceLoaderOptions LoaderOpts(FEOpts); + StringRef ABIPath = Instance.getPrimarySpecificPathsForAtMostOnePrimary() + .SupplementaryOutputs.ABIDescriptorOutputPath; return ModuleInterfaceLoader::buildSwiftModuleFromSwiftInterface( Instance.getSourceMgr(), Instance.getDiags(), Invocation.getSearchPathOptions(), Invocation.getLangOptions(), Invocation.getClangImporterOptions(), Invocation.getClangModuleCachePath(), PrebuiltCachePath, - Invocation.getModuleName(), InputPath, Invocation.getOutputFilename(), + Invocation.getModuleName(), InputPath, Invocation.getOutputFilename(), ABIPath, FEOpts.SerializeModuleInterfaceDependencyHashes, FEOpts.shouldTrackSystemDependencies(), LoaderOpts, RequireOSSAModules_t(Invocation.getSILOptions())); diff --git a/test/ModuleInterface/emit-abi-descriptor.swift b/test/ModuleInterface/emit-abi-descriptor.swift new file mode 100644 index 0000000000000..530d5e5b110ec --- /dev/null +++ b/test/ModuleInterface/emit-abi-descriptor.swift @@ -0,0 +1,13 @@ +// RUN: %empty-directory(%t) +// RUN: %empty-directory(%t/Foo.swiftmodule) +// RUN: %empty-directory(%t/ResourceDir/%target-sdk-name/prebuilt-modules/Foo.swiftmodule) +// RUN: echo "public func foo() {}" > %t/Foo.swift + +// RUN: %target-swift-frontend -emit-module %t/Foo.swift -module-name Foo -emit-module-interface-path %t/Foo.swiftinterface +// RUN: %target-swift-frontend -compile-module-from-interface %t/Foo.swiftinterface -o %t/Foo.swiftmodule -module-name Foo -emit-abi-descriptor-path %t/Foo.json + +// RUN: %FileCheck %s < %t/Foo.json + +// CHECK: "kind": "Root" +// CHECK-NEXT: "name": "TopLevel" +// CHECK-NEXT: "printedName": "TopLevel" From 15d1e0a535bd70522f73e9fa3a198d6d74514bb0 Mon Sep 17 00:00:00 2001 From: Xi Ge Date: Mon, 23 Aug 2021 14:21:08 -0700 Subject: [PATCH 4/5] ABIChecker: diagnose mangled name changes Moving generic constraint from extension to its member or vice versa may change the mangled name of the member without changing the generic signature, thus introducing ABI breakages. This change teaches the ABI checker to diagnose USR (mangled name) changes to cover such cases. rdar://78276290 --- include/swift/AST/DiagnosticsModuleDiffer.def | 2 ++ include/swift/AST/USRGeneration.h | 3 +++ lib/APIDigester/ModuleAnalyzerNodes.cpp | 9 ++++++++ lib/APIDigester/ModuleDiagsConsumer.cpp | 1 + lib/AST/USRGeneration.cpp | 13 ++++++++++++ test/api-digester/Outputs/Cake-abi.txt | 11 +++++++++- test/api-digester/diff-demangled-name.swift | 21 +++++++++++++++++++ 7 files changed, 59 insertions(+), 1 deletion(-) create mode 100644 test/api-digester/diff-demangled-name.swift diff --git a/include/swift/AST/DiagnosticsModuleDiffer.def b/include/swift/AST/DiagnosticsModuleDiffer.def index dbcc194ba4ca6..4c66365d13ac9 100644 --- a/include/swift/AST/DiagnosticsModuleDiffer.def +++ b/include/swift/AST/DiagnosticsModuleDiffer.def @@ -88,6 +88,8 @@ ERROR(not_inheriting_convenience_inits,none,"%0 no longer inherits convenience i ERROR(enum_case_added,none,"%0 has been added as a new enum case", (StringRef)) +ERROR(demangled_name_changed,none,"%0 has mangled name changing from '%1' to '%2'", (StringRef, StringRef, StringRef)) + WARNING(cannot_read_allowlist,none,"cannot read breakage allowlist at '%0'", (StringRef)) #define UNDEFINE_DIAGNOSTIC_MACROS diff --git a/include/swift/AST/USRGeneration.h b/include/swift/AST/USRGeneration.h index 7b96c4d3e3c27..4d4f116c0154c 100644 --- a/include/swift/AST/USRGeneration.h +++ b/include/swift/AST/USRGeneration.h @@ -61,6 +61,9 @@ bool printExtensionUSR(const ExtensionDecl *ED, raw_ostream &OS); /// \returns true if it failed, false on success. bool printDeclUSR(const Decl *D, raw_ostream &OS); +/// Demangle a mangle-name-based USR to a human readable name. +std::string demangleUSR(StringRef mangled); + } // namespace ide } // namespace swift diff --git a/lib/APIDigester/ModuleAnalyzerNodes.cpp b/lib/APIDigester/ModuleAnalyzerNodes.cpp index 7ca00d58f4dc1..a1dc551375c2d 100644 --- a/lib/APIDigester/ModuleAnalyzerNodes.cpp +++ b/lib/APIDigester/ModuleAnalyzerNodes.cpp @@ -944,6 +944,8 @@ static bool isSDKNodeEqual(SDKContext &Ctx, const SDKNode &L, const SDKNode &R) if (Left->getFixedBinaryOrder() != Right->getFixedBinaryOrder()) return false; } + if (Left->getUsr() != Right->getUsr()) + return false; LLVM_FALLTHROUGH; } case SDKNodeKind::Conformance: @@ -2551,6 +2553,13 @@ void swift::ide::api::SDKNodeDecl::diagnose(SDKNode *Right) { emitDiag(Loc, diag::decl_reorder, getFixedBinaryOrder(), RD->getFixedBinaryOrder()); } + if (getUsr() != RD->getUsr()) { + auto left = demangleUSR(getUsr()); + auto right = demangleUSR(RD->getUsr()); + if (left != right) { + emitDiag(Loc, diag::demangled_name_changed, left, right); + } + } } } diff --git a/lib/APIDigester/ModuleDiagsConsumer.cpp b/lib/APIDigester/ModuleDiagsConsumer.cpp index 044c9d05926d9..c73fd148d5947 100644 --- a/lib/APIDigester/ModuleDiagsConsumer.cpp +++ b/lib/APIDigester/ModuleDiagsConsumer.cpp @@ -54,6 +54,7 @@ static StringRef getCategoryName(uint32_t ID) { case LocalDiagID::raw_type_change: return "/* RawRepresentable Changes */"; case LocalDiagID::generic_sig_change: + case LocalDiagID::demangled_name_changed: return "/* Generic Signature Changes */"; case LocalDiagID::enum_case_added: case LocalDiagID::decl_added: diff --git a/lib/AST/USRGeneration.cpp b/lib/AST/USRGeneration.cpp index c3fe2322c21c9..5a02175895e8f 100644 --- a/lib/AST/USRGeneration.cpp +++ b/lib/AST/USRGeneration.cpp @@ -19,6 +19,7 @@ #include "swift/AST/SwiftNameTranslation.h" #include "swift/AST/TypeCheckRequests.h" #include "swift/AST/USRGeneration.h" +#include "swift/Demangling/Demangler.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/raw_ostream.h" @@ -255,6 +256,18 @@ swift::USRGenerationRequest::evaluate(Evaluator &evaluator, return NewMangler.mangleDeclAsUSR(D, getUSRSpacePrefix()); } +std::string ide::demangleUSR(StringRef mangled) { + if (mangled.startswith(getUSRSpacePrefix())) { + mangled = mangled.substr(getUSRSpacePrefix().size()); + } + SmallString<128> buffer; + buffer += "$s"; + buffer += mangled; + mangled = buffer.str(); + Demangler Dem; + return nodeToString(Dem.demangleSymbol(mangled)); +} + std::string swift::MangleLocalTypeDeclRequest::evaluate(Evaluator &evaluator, const TypeDecl *D) const { diff --git a/test/api-digester/Outputs/Cake-abi.txt b/test/api-digester/Outputs/Cake-abi.txt index 6efaa9a4c05bc..7010f162d7149 100644 --- a/test/api-digester/Outputs/Cake-abi.txt +++ b/test/api-digester/Outputs/Cake-abi.txt @@ -1,7 +1,17 @@ /* Generic Signature Changes */ +cake: Constructor S1.init(_:) has mangled name changing from 'cake.S1.init(Swift.Int) -> cake.S1' to 'cake.S1.init(Swift.Double) -> cake.S1' +cake: Func C1.foo1() has mangled name changing from 'static cake.C1.foo1() -> ()' to 'cake.C1.foo1() -> ()' +cake: Func C1.foo2(_:) has mangled name changing from 'cake.C1.foo2(Swift.Int) -> ()' to 'cake.C1.foo2(() -> ()) -> ()' cake: Func P1.P1Constraint() has generic signature change from to +cake: Func P1.P1Constraint() has mangled name changing from '(extension in cake):cake.P1< where A: cake.P2>.P1Constraint() -> ()' to '(extension in cake):cake.P1.P1Constraint() -> ()' +cake: Func S1.foo3() has mangled name changing from 'cake.S1.foo3() -> ()' to 'static cake.S1.foo3() -> ()' +cake: Func S1.foo5(x:y:) has mangled name changing from 'cake.S1.foo5(x: Swift.Int, y: Swift.Int) -> ()' to 'cake.S1.foo5(x: Swift.Int, y: Swift.Int, z: Swift.Int) -> ()' +cake: Func Somestruct2.foo1(_:) has mangled name changing from 'static cake.Somestruct2.foo1(cake.C3) -> ()' to 'static cake.NSSomestruct2.foo1(cake.C1) -> ()' +cake: Func ownershipChange(_:_:) has mangled name changing from 'cake.ownershipChange(inout Swift.Int, __shared Swift.Int) -> ()' to 'cake.ownershipChange(Swift.Int, __owned Swift.Int) -> ()' +cake: Func returnFunctionTypeOwnershipChange() has mangled name changing from 'cake.returnFunctionTypeOwnershipChange() -> (cake.C1) -> ()' to 'cake.returnFunctionTypeOwnershipChange() -> (__owned cake.C1) -> ()' cake: Protocol P3 has generic signature change from to +cake: Struct Somestruct2 has mangled name changing from 'cake.Somestruct2' to 'cake.NSSomestruct2' /* RawRepresentable Changes */ @@ -112,6 +122,5 @@ cake: Var RequiementChanges.addedVar has been added as a protocol requirement /* Class Inheritance Change */ cake: Class SubGenericClass has changed its super class from cake.GenericClass to cake.GenericClass cake: Class SuperClassRemoval has removed its super class cake.C3 -cake: Class SuperClassRemoval no longer inherits convenience inits from its superclass cake: Constructor AddingNewDesignatedInit.init(_:) has been added as a designated initializer to an open class cake: Constructor ClassWithMissingDesignatedInits.init() has been added as a designated initializer to an open class diff --git a/test/api-digester/diff-demangled-name.swift b/test/api-digester/diff-demangled-name.swift new file mode 100644 index 0000000000000..3a5d8290508d4 --- /dev/null +++ b/test/api-digester/diff-demangled-name.swift @@ -0,0 +1,21 @@ +// RUN: %empty-directory(%t) +// RUN: echo "public func foo() {}" > %t/Foo.swift + +// RUN: echo "public protocol P { associatedtype A }" > %t/Foo-1.swift +// RUN: echo "public extension P { public func f() where A == Int {} }" >> %t/Foo-1.swift + +// RUN: echo "public protocol P { associatedtype A }" > %t/Foo-2.swift +// RUN: echo "public extension P where A == Int { public func f() {} }" >> %t/Foo-2.swift + +// RUN: %target-swift-frontend -emit-module %t/Foo-1.swift -module-name Foo -emit-module-interface-path %t/Foo1.swiftinterface +// RUN: %target-swift-frontend -emit-module %t/Foo-2.swift -module-name Foo -emit-module-interface-path %t/Foo2.swiftinterface + +// RUN: %target-swift-frontend -compile-module-from-interface %t/Foo1.swiftinterface -o %t/Foo1.swiftmodule -module-name Foo -emit-abi-descriptor-path %t/Foo1.json + +// RUN: %target-swift-frontend -compile-module-from-interface %t/Foo2.swiftinterface -o %t/Foo2.swiftmodule -module-name Foo -emit-abi-descriptor-path %t/Foo2.json + +// RUN: %api-digester -diagnose-sdk -print-module --input-paths %t/Foo1.json -input-paths %t/Foo2.json -abi -o %t/result.txt + +// RUN: %FileCheck %s < %t/result.txt + +// CHECK: Foo: Func P.f() has mangled name changing from '(extension in Foo):Foo.P.f< where A.A == Swift.Int>() -> ()' to '(extension in Foo):Foo.P< where A.A == Swift.Int>.f() -> ()' From 4b4dee29cb95a69497c4ddce3883f33b78017173 Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Tue, 24 Aug 2021 13:57:43 -0700 Subject: [PATCH 5/5] AST: repair the build after #39010 There is a reference to `std::string` being introduced without the necessary header being included. Add the missing include. --- include/swift/AST/USRGeneration.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/swift/AST/USRGeneration.h b/include/swift/AST/USRGeneration.h index 4d4f116c0154c..5f799a06abd02 100644 --- a/include/swift/AST/USRGeneration.h +++ b/include/swift/AST/USRGeneration.h @@ -21,6 +21,8 @@ #include "swift/Basic/LLVM.h" +#include + namespace swift { class Decl; class AbstractStorageDecl;